所以我正在努力建立一个&#34;消毒剂&#34;用于在格式化参数转发到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*
都应该&#34;衰变&#34;进入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
,这就是我返回decltype(auto)
的原因。但我无法真正理解如何实施这一目标。
1)std::forward
是否正确?它应该保留std::string&&
的返回的r值参考,对吗?
2)我应该如何专门化我的模板:std::string
或 this.db.collection('users').doc('uid')
.get().then(
doc => {
if (doc.exists) {
this.db.collection('users').doc('uid').collection('friendsSubcollection').get().
then(sub => {
if (sub.docs.length > 0) {
console.log('subcollection exists');
}
});
}
});
?
3)推导的转发引用应该与实际类型相同,对吗?
答案 0 :(得分:4)
你几乎完全误解了转发引用;这是可以预料的,它们令人困惑。
要理解它们,您必须了解参考折叠规则,std::forward
本身,推导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&
。
重读。再次。再一次。 <{1}}在两者都适用时胜过&
。
接下来,扣除。我会在这里说谎,但他们正在简化谎言。
如果您将&&
传递给模板推断Foo&
,则X&&
为X
而Foo&
为X&&
。
如果您将Foo&
传递给模板推断Foo&&
,则X&&
为X
而Foo
为X&&
。
接下来,Foo&&
是一个有条件的举动。对于引用变量std::forward
,X&& x
为std::forward<X>(x)
- 它会将decltype(x)(x)
强制转换为声明为的类型。如果x
是右值引用,则会将x
强制转换为右值引用。这是必需的,因为即使x
是右值引用,表达式x
的类型仍为X&
而非X&&
。右值引用是一个引用到一个右值,但它本身不是一个右值。
现在修复你的代码。
x
我遵循SRP(单一责任原则) - 我从消毒中分离出来。
然后我将实际执行的清理操作(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))...);
}
)分开(sanitizer
)。
这让我写一次sanitize
santization代码,没有完美的转发胜利“偶然”。
顺便说一句,如果你想将数组args视为非指针,我会用remove ref替换decay并删除cv。
您可以通过在std::string
的命名空间或我们正在清理的类型的命名空间中编写sanitize
重载来扩展sanitizer
功能(自然不在tag_t
中)。
答案 1 :(得分:2)
所以我正在尝试构建一个“清理程序”函数,以便在格式化参数转发到
printf
之前对其进行过滤。
你的前提似乎错了。 printf
是一个使用va_arg
的C函数 - 它不会或不需要任何完美转发。
http://en.cppreference.com/w/cpp/io/c/fprintf
它也永远不会“消耗”它的论点,而只是从它们中读取。区分临时和非临时性是没有意义的 - 只需在const Args&...
包装中取printf
。
1)
decltype(auto)
是否正确?它应该保留std::forward
的返回的r值参考,对吗?
std::forward
将返回左值引用或右值引用。 decltype(auto)
确实会保留这一点。
sanitize_forward
对std::string
的专长看起来并不有用 - std::forward<const char*>
将始终返回const char* &&
。我认为它不同于:
template<>
_CR_INLINE const char* sanitize_forward<std::string>(std::string&& arg) {
return arg.c_str();
}
此外,将右值引用的.c_str()
返回到std::string
听起来非常危险且不正确:您正在使用指向字符串内部缓冲区的指针直到到期为止。您可能想在此处const std::string&
。
2)我应该如何专门化我的模板:
std::string&&
或std::string
?
如何调用它?您是否明确提供模板参数?模板参数是否始终是非引用,还是左值引用和非引用?
由于你有sanitize_forward<Args>
,你可能会尝试同时调用两个......
sanitize_forward<std::string>
sanitize_forward<std::string&>
...也许与 cv-qualifiers 。您可能希望提供一个额外的显式std::decay_t<Args>
参数来处理“专业化”业务。
3)推导的转发引用应该与实际类型相同,对吗?
不确定你的意思。你能详细说明一下吗?