模板专业化和完美的转发
Template specialization and perfect forwarding
,所以我正在尝试构建"消毒器"功能以滤除格式的参数,然后再将其转发到 printf
。
template<typename A>
_CR_INLINE decltype(auto) sanitize_forward(A&& arg) {
return std::forward<A>(arg);
}
template<>
_CR_INLINE decltype(auto) sanitize_forward<std::string>(std::string&& arg) {
return std::forward<const char*>(arg.c_str());
}
因此,每个 std::string
都应该"衰减"到 const char*
中,以便正确格式化。
template<typename...Args>
_CR_INLINE void printf_safe(const std::string& format, Args&&...args) {
std::printf(format.c_str(), sanitize_forward<Args>(args)...);
}
我希望参数完美转发到 printf
,这就是为什么我返回 std::forward
的原因。但是我不能真正围绕如何实现这一点。
1)decltype(auto)
正确吗?它应该保留std::forward
返回的R值参考性,对吗?
2)我应该如何专业化模板: std::string&&
或 std::string
?
3)推导的转发参考应与实际类型相同,对吧?
您几乎完全误解了转发参考;这是可以预料的,它们会令人困惑。
要理解它们,您必须了解参考折叠规则,std::forward
本身,DEMATED T&&
时的模板参数扣除规则,以及它们至少在某种程度上如何将它们结合在一起。
首先,参考崩溃。查看此图表:
If Then Then
X is X& is X&& is
T T& T&&
T& T& T&
T const& T const& T const&
T&& T& T&&
如果X
是int
,则X&&
为int&&
。如果X
是int&
,则X&&
为int&
。
重读。再次。再一次。&
在应用两者时都胜出&&
。
接下来,推论。我会在这里说谎,但他们正在简化谎言。
如果将Foo&
传递到DEADUC X&&
的模板,则X
为Foo&
,X&&
为Foo&
。
如果将Foo&&
传递到DEADUC X&&
的模板,则X
为Foo
,X&&
是Foo&&
。
接下来,std::forward
是一个有条件的举动。对于参考变量X&& x
,std::forward<X>(x)
为decltype(x)(x)
-它将x
施加到被声明为类型的类型。如果x
是RVALUE参考,则将x
投入到RVALUE参考。这是需要的,因为即使x
是RVALUE参考,即使x
表达式CC_41的类型也不是X&&
。rvalue参考是 to rvalue,但不是rvalue。
现在修复您的代码。
template<class T>
struct tag_t{constexpr tag_t(){}};
template<class T>
constexpr tag_t<std::decay_t<T>> tag{};
template<class T>
auto sanitizer(tag_t<T>){
return [](auto&& t)->decltype(auto){
return decltype(t)(t);
};
}
template<class A>
decltype(auto) sanitize(A&& arg) {
return sanitizer(tag<A>)(std::forward<A>(arg));
}
auto sanitizer(tag_t<std::string>) {
return [](std::string const& s){return s.c_str();};
}
template<class...Ts>
void printf_safe(const std::string& format, Ts&&...ts) {
std::printf(format.c_str(), sanitize(std::forward<Ts>(ts))...);
}
我遵循SRP(单个责任原则) - 我从消毒中向前拆下。
然后,我将哪个消毒动作(sanitizer
)分开(sanitize
)。
这让我写了一次 std::string
santization代码,而无需"偶然"赢得完美的胜利。
顺便说一句,如果要将Array Arg视为非分子,我将用Remove Ref和删除CV替换。
您可以通过在tag_t
的命名空间或我们正在消毒的类型的命名空间中编写sanitizer
过载来扩展sanitize
功能(自然而然地在std
中)。
所以我正在尝试构建"消毒器"功能,以在将其转发到
printf
之前过滤格式化参数。
您的前提似乎是错误的。printf
是使用va_arg
的C函数 - 它不做或需要任何完美的转发。
http://en.cppreference.com/w/cpp/io/c/fprintf
它也永远不会"消耗"其论点,而只是从中读取。区分临时工和非陪伴是没有意义的 - 只需在printf
包装中服用const Args&...
。
1)
decltype(auto)
正确吗?它应该保留std::forward
返回的R值参考性,对吗?
std::forward
将返回 lvalue参考或 rvalue参考。decltype(auto)
确实会保留。
您对std::string
的sanitize_forward
的专业化看起来并不有用 - std::forward<const char*>
将始终返回const char* &&
。我认为这与:
template<>
_CR_INLINE const char* sanitize_forward<std::string>(std::string&& arg) {
return arg.c_str();
}
此外,将 rvalue参考的 .c_str()
返回到 std::string
听起来很危险和不正确:您正在将即将到期的字符串的内部缓冲区进行指针。您可能想在这里服用const std::string&
。
2)我应该如何专业化我的模板:
std::string&&
或std::string
?
它将如何称呼?您是否明确提供模板参数?模板参数是始终是 non-reference ,还是将既是 lvalue参考又是 non-non-Reference ?
由于您有sanitize_forward<Args>
,您可能会尝试调用这两个...
sanitize_forward<std::string>
sanitize_forward<std::string&>
...也许使用 cv-qualifiers 。您可能需要提供额外的明确std::decay_t<Args>
参数,该参数涉及"专业"业务。
3)推导的转发参考应与实际类型相同,对吧?
不确定您的意思。您能详细说明吗?
- 将函数参数完美转发到函数指针:按值传递呢?
- C++20理念:要求表达和完美转发
- 完美的转发和构造函数
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 返回值的完美转发?
- 使用衰减与完美转发
- 可变参数模板:将整数参数完美转发到 lambda
- 完美转发C++重载和模板化函子及其参数
- 在完美转发中需要衰减
- C++完美转发:如何避免悬空引用
- 完美的对象转发阵列
- 无法理解一段具有完美转发和省略号的C++代码
- 在编写包装现有函数并检查错误的模板函数时,如何使用完美转发?
- 完美转发可变参数模板模板
- 在完美转发函数中公开参数类型,避免代码重复
- 完美的转发构造函数和已删除的构造函数
- 完美的转发功能推断出冲突错误
- 使用完美转发的模板转换构造函数
- 完美转发使用结构化绑定声明的变量
- 完美转发常量参数以进行持续评估