了解别名模板

Understanding Alias Templates

我问了一个 有几个参考代码:

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

我相信我有一个普遍的误解alias templates:

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

上面的代码只是一次对多个模板参数执行 enable_if_t 吗?

其次,我认为我对void_t的作用存在特定的误解。 指出 C++17 标准定义了 void_t。这是我没有得到的:

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

在你的链接问题中 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,以便它与 primary 的默认参数相匹配模板定义。 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 在您的实施中可用,则无需重新定义它。当它被标准化后,它将像标准中的任何其他别名模板一样可用。

这样想:如果 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 { };

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

现在想想Tstruct 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

我认为显示的示例并没有真正说明 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 { };

对于这个语句:

The former version is much easier to read and void_t doesn't require decltype to work.

我认为在可读性方面的优势很小,第二部分没有意义,当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 真正有用的地方,其他用途只是一个小的改进,它们提高了一致性。