在您链接问题的巴里示例中:
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
to推导的类型,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
isint
具有有效的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
.