我问了一个question,它有几个对代码的引用:
template <typename...>
using void_t = void;
我相信我一般会误解alias templates:
您为什么不评估您在enable_if_t
或conditional_t
声明中传递到别名模板的模板参数?
上面的代码是否只是一次对多个模板参数执行enable_if_t
?
其次,我认为我对void_t
的作用有一个特定的误解。 This comment声明C ++ 17标准定义了void_t
。这是我不能得到的:
Isn&#39; t void_t
只是一个任意名称?如果我仍然需要在我计划使用template <typename...> using void_t = void;
的任何地方定义void_t
,那么标准化任意名称的目的是什么?
答案 0 :(得分: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 { };
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())
以前的版本更容易阅读,void_t
不需要decltype
才能使用。
如果您的实施中有void_t
,则无需重新定义。当它标准化时,它将像标准中的任何其他别名模板一样可用。
以这种方式思考:如果T
为int
,其有效期为std::to_string
,则扣除将如下所示:
has_to_string<int>
- &gt; 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
。
答案 1 :(得分: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
真正有用的地方,其他用途只是一个小改进,它们可以提高一致性。