4

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

template <typename...>
using void_t = void;

我相信我对别名模板有一个普遍的误解:

您为什么不只评估您在enable_if_torconditional_t语句中传递给别名模板的任何模板参数?

上面的代码是否只是一次enable_if_t对多个模板参数进行处理?

其次,我相信我对角色的具体理解有误void_t此评论指出 C++17 标准定义了void_t. 这是我不明白的:

void_t就是一个随意的名字吗?如果我仍然必须定义template <typename...> using void_t = void;我计划使用的任何地方void_t,那么标准化任意名称的意义何在?

4

2 回答 2

8

在您链接问题的巴里示例中:

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 { };

void_t仅用于转换由decltypeto推导的类型,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())

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

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

可以这样想:如果Tisint具有有效的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 { };

好的,这是一些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

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

template<>
struct has_to_string<int,void>
: std::true_type { };

这与我们的 , 的实例化相匹配has_string<int,void>,因此has_to_string<int>继承自std::true_type

现在想想是什么T时候struct Foo{};。同样,让我们​​计算出依赖类型:

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

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

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

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

于 2015-05-18T11:55:26.070 回答
3

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

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

它与

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

对于这个声明:

前一个版本更容易阅读并且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 { }

如您所见,即使是主模板也用于void_t增加可读性,因为它现在与专业化匹配。这不是绝对必要的,但我喜欢它。当您考虑替代方案时,真正的力量就来了。没有void_t,专业化现在更加复杂:

template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }

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

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

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

于 2015-05-19T19:30:25.460 回答