编辑:this我的另一个问题集中在这个问题的简化版本上,可能更容易理解。
我写了一个小代码片段来重现std::experimental::is_detected
(here)的行为。我的实现基本上取自cppreference,但我摆脱了Default
模板参数。
我的问题是:在以下代码段中,为什么has_type
(要检查的条件)有是using
声明,并且不能,例如结构(在这种情况下is_detected
返回错误的结果)?
/***** is_detected definition *****/
template<typename...Args>
using void_t = void;
template<typename Void, template<class...> class Op, typename ...Args>
struct Detector {
static constexpr bool value = false;
};
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t<Op<Args...>>, Op, Args...> {
static constexpr bool value = true;
};
template<template<class...> class Op, typename...Args>
using is_detected_t = Detector<void, Op, Args...>;
/****************************/
/***** is_detected test *****/
// two dummy types on which to test a condition
struct Yes { using type = void; };
struct No { };
// the condition to test
template<typename T>
using has_type = typename T::type;
// struct has_type { using type = typename T::type; }; // does not work as intended!
int main() {
static_assert(is_detected_t<has_type, Yes>::value, "");
static_assert(!is_detected_t<has_type, No>::value, "");
return 0;
}
答案 0 :(得分:1)
可能有助于了解探测器实际使用has_type
的方式:
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t< Op<Args...>>, Op, Args...> {
// ^^ ^^
// has_type Yes/No
static constexpr bool value = true;
};
对于匹配编译器的此特化,必须确保Op<Args...>
在用实际参数(Op
和{{替换参数(Args...
和has_type
)时1}} / Yes
),必须命名类型(因为那是模板No
所需要的第一个模板参数)。
由于void_t
不是某种类型,而是某种类型的别名,因此必须查看别名是否为某种类型命名。
对于has_type
,这将是Yes
,这也是Yes::type
的别名。 void
是一种类型,所以一切都很好,专业化匹配,void
是value
。
对于true
,这将是No
,它不存在(No::type
毕竟没有成员No
。因此,替换失败(但这不是错误,SFINAE),不能使用特化。因此,编译器选择基本模板,type
为value
。
现在,如下定义false
会发生什么:
has_type
然后需要(在template<typename T>
struct has_type { using type = typename T::type; }
情况下)需要No
类型的特殊化需求。 has_type<No>
是一个类模板,它给出了一些类型(has_type
是一个类型,所以一切都很好)&#34;产生&#34;一种。因此,No
是一种类型。因此,专业化匹配,has_type<No>
为value
。
此时不需要true
的成员。您甚至可以使用has_type<No>
(只有声明,没有定义)。换句话说,它可能是一个不完整的类型:
类型模板参数的模板参数必须是type-id,可以命名不完整的类型[..]
http://en.cppreference.com/w/cpp/language/template_parameters
内容仅在编译器实际需要它们时才有用,例如用于创建该类型的对象:
template<typename> struct has_type;
这种机制通常用于&#34;类型标签&#34;可用于创建具有相同内容的不同类型&#34;从单个模板:
// Class template with some random members.
template<typename T>
struct Foo {
using baz = typename T::baz;
constexpr static int value = T::value * 42;
};
// Class template which is even only declared
template<typename X> struct Bar; // no definition
// Does not use its template parameter in any way. Needs just a type name.
template<typename> struct Defer {};
int main() {
Defer<Foo<int>> ok;
Defer<Bar<int>> ok_too;
// Foo<int> fail;
// Bar<int> fail_too;
return 0;
}
template<typename /* TAG */, typename ValueType>
struct value_of_strong_type {
ValueType value;
// ...
};
struct A_tag; // no definition
using A = value_of_strong_type<A_tag, int>;
struct B_tag; // no definition
using B = value_of_strong_type<B_tag, int>;
和A
的行为相同,但彼此无法互换,因为它们的类型完全不同。
要使检测器与您展示的类模板一起使用,您需要以下专业化:
B
虽然你不能只是添加它,否则你会遇到模糊的分辨率错误。