模板专业化和完美转发

时间:2017-12-29 01:55:58

标签: c++ c++11

所以我正在努力建立一个&#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)推导的转发引用应该与实际类型相同,对吗?

2 个答案:

答案 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&&

如果Xint,则X&&int&&。如果Xint&,则X&&int&

重读。再次。再一次。 <{1}}在两者都适用时胜过&

接下来,扣除。我会在这里说谎,但他们正在简化谎言。

如果您将&&传递给模板推断Foo&,则X&&XFoo&X&&

如果您将Foo&传递给模板推断Foo&&,则X&&XFooX&&

接下来,Foo&&是一个有条件的举动。对于引用变量std::forwardX&& xstd::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_forwardstd::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)推导的转发引用应该与实际类型相同,对吗?

不确定你的意思。你能详细说明一下吗?