了解别名模板

Jon*_*Mee 4 c++ alias templates enable-if c++17

我问了一个有几个代码引用的问题:

template <typename...>
using void_t = void;
Run Code Online (Sandbox Code Playgroud)

我相信我有一个误解别名模板:

为什么不评估您在enable_if_tor conditional_t语句中传递给别名模板的模板参数?

以上代码是否只是一次enable_if_t执行多个模板参数?

其次,我认为我对这个角色有一个特定的误解void_t.该评论指出C++ 17标准定义void_t.这是我没有得到的:

void_t只是一个任意的名字?如果我还要定义template <typename...> using void_t = void;我计划使用void_t的任何地方,那么标准化任意名称的重点是什么?

Tar*_*ama 8

在Barry的例子中,来自您的链接问题:

template<typename T, typename = void>
struct has_to_string
: std::false_type { };

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };
Run Code Online (Sandbox Code Playgroud)

void_t仅用于转换推导出的类型decltype,void以便它将默认参数与模板定义匹配.SFINAE完全由decltype表达式处理.您可以轻松地执行以下操作:

//use , void() instead of wrapping in void_t
//this uses the comma operator to check the type of the to_string call, then change the type to void
decltype(std::to_string(std::declval<T>()), void())
Run Code Online (Sandbox Code Playgroud)

以前的版本更容易阅读,void_t不需要decltype工作.

如果void_t在您的实现中可用,则无需重新定义它.当它标准化时,它将像标准中的任何其他别名模板一样可用.

以这种方式思考:如果Tint,具有有效的std::to_string重载,则演绎将如下所示:

has_to_string<int>- > has_to_string<int,void>因为默认参数.所以让我们来寻找has_to_string那些参数的特化.

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };
Run Code Online (Sandbox Code Playgroud)

好的,这是某些T和某些依赖类型的部分特化.让我们找出那种类型:

void_t<decltype(std::to_string(std::declval<T>()))>
//std::to_string(int&&) is valid and returns a std::string
void_t<std::string>
//void_t changes types to void
void
Run Code Online (Sandbox Code Playgroud)

现在我们的专业化看起来像这样:

template<>
struct has_to_string<int,void>
: std::true_type { };
Run Code Online (Sandbox Code Playgroud)

这符合我们的实例化has_string<int,void>,所以has_to_string<int>继承自std::true_type.

现在想来当Tstruct Foo{};.再次,让我们计算出依赖类型:

void_t<decltype(std::to_string(std::declval<T>()))>
//wait, std::to_string(Foo&&) doesn't exist
//discard that specialization
Run Code Online (Sandbox Code Playgroud)

丢弃该专业化后,我们回到主要模板:

template<typename T, typename = void>
struct has_to_string
: std::false_type { };
Run Code Online (Sandbox Code Playgroud)

所以has_to_string<Foo>继承自std::false_type.


Dan*_*rey 3

我认为所显示的示例并没有真正显示出什么void_t是好的,因为它只显示了一个用例,但是当您查看

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };
Run Code Online (Sandbox Code Playgroud)

它与

template<typename T>
struct has_to_string<T, 
    decltype(std::to_string(std::declval<T>()), void())
    > 
: std::true_type { };
Run Code Online (Sandbox Code Playgroud)

对于这个声明:

前一个版本更容易阅读并且void_t不需要decltype工作。

我认为可读性方面的优势很小,第二部分毫无意义,当decltype不起作用时,SFINAE 就会按预期启动。

void_t提案中的一个更有用的例子是:

// primary template handles types that have no nested ::type member
template< class, class = void_t<> >
struct has_type_member
: std::false_type { };

// specialization recognizes types that do have a nested ::type member
template< class T >
struct has_type_member<T, void_t<typename T::type>>
: std::true_type { }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,即使是主模板也用来void_t增加可读性,因为它现在与专业化相匹配。这并不是绝对必要的,但我喜欢它。当你考虑其他选择时,真正的力量就会出现。如果没有void_t,专业化现在会更加复杂:

template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }
Run Code Online (Sandbox Code Playgroud)

不能用作T::type类型名称,而不是表达式。因此你需要

template< class T >
struct has_type_member<T, decltype(std::declval<typename T::type>(), void())>
: std::true_type { }
Run Code Online (Sandbox Code Playgroud)

整个表达式变得更长、更棘手,并且可能会遇到您忘记处理的边缘情况。这才是void_t真正有帮助的地方,其他用途只是一个小小的改进,它们可以提高一致性。