检测成语:为什么条件必须是`using`指令?

时间:2017-05-18 07:51:19

标签: c++ metaprogramming

编辑:this我的另一个问题集中在这个问题的简化版本上,可能更容易理解。

我写了一个小代码片段来重现std::experimental::is_detectedhere)的行为。我的实现基本上取自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;
}

1 个答案:

答案 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是一种类型,所以一切都很好,专业化匹配,voidvalue

对于true,这将是No,它不存在(No::type毕竟没有成员No。因此,替换失败(但这不是错误,SFINAE),不能使用特化。因此,编译器选择基本模板,typevalue

现在,如下定义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

虽然你不能只是添加它,否则你会遇到模糊的分辨率错误。