在巴里的链接问题的示例中:
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
以便它与默认参数匹配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
上班。
If void_t
在您的实现中可用,您无需重新定义它。当它标准化后,它将像标准中的任何其他别名模板一样可用。
这样想:如果T
is int
,其中有一个有效的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>
, so has_to_string<int>
继承自std::true_type
.
现在想想当T
is 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 { };
So has_to_string<Foo>
继承自std::false_type
.