is_enum 导致 SFINAE 应用程序出现错误行为?

is_enum causing incorrect behavior for SFINAE application?

我一直在尝试将 SFINAE 应用于 "has_member" 类型的结构,如 here 所述。

所以我试图使用 c++11 的一些特性来简化这些解决方案。在检查枚举是否存在时遇到一些问题,但我似乎无法在此处找到我的逻辑错误。代码是:

#include<iostream>

template<typename T >
struct has_enum_name
{
private:
  // ***** this is the line that isn't working *****
  // If U has an enum "name", then I expect is_enum to return true…
  // Then I expect enable_if<>::type to be std::true_type…etc. This isn't what happens.
  template<typename U> static auto test(int) -> decltype(
    std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type() );

  template<typename U> static auto test(...) -> std::false_type;
public:
  static constexpr bool value = 
    !(std::is_same<decltype(test<T>(0)),std::false_type>::value);

  static constexpr bool is_enum()
  {
    return std::is_enum<typename T::name>::value;
  }
};

template<typename T >
struct has_enum_name_2
{
private:
  template<typename U> static auto test(int) -> decltype( 
    std::enable_if<true, std::true_type>::type() );

  template<typename U> static auto test(...) -> std::false_type;
public:
  static constexpr bool value = 
    !(std::is_same<decltype(test<T>(0)),std::false_type>::value);
};

struct Foo
{
  enum class name
  {
    enum1,
    enum2
  };
};

int main()
{
  std::cout<<"std::is_enum<Foo::name>::value = "<<std::is_enum<Foo::name>::value<<std::endl;
  std::cout<<"has_enum_name<Foo>::value      = "<<has_enum_name<Foo>::value<<std::endl;
  std::cout<<"has_enum_name<Foo>::is_enum()  = "<<has_enum_name<Foo>::is_enum()<<std::endl;
  std::cout<<"has_enum_name_2<Foo>::value    = "<<has_enum_name_2<Foo>::value<<std::endl;
}

运行 使用 gcc 4.9.2 给出

$ ./a.out 
std::is_enum<Foo::name>::value = 1
has_enum_name<Foo>::value      = 0
has_enum_name<Foo>::is_enum()  = 1
has_enum_name_2<Foo>::value    = 1

第一行输出验证 std::is_enum 对于 Foo::name 枚举是否正确工作。

第二行输出struct constexpr的结果"has_enum_name::value"。我只是想将 std::enable_if 与 std::is_enum 结合使用,使 decltype return 成为某种东西……在本例中为 std::true_type()。如您所见,输出 returns false...所以行:

template<typename U> static auto test(int) -> decltype( 
  std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type() );

没有像我认为的那样锻炼。我认为 is_enum 应该 return 为真,也就是说 enable_if 应该 return true_type,decltype 应该 return true_type( ).

下一个输出只是检查 std::is_enum<> 是否在结构中正常工作……是的。

下一个输出只是 "has_enum_name" 结构的一个副本,但我用硬编码 "true" 替换了可疑行中的 is_enum<> ......并且它工作正常。

因此,出于某种原因,is_enum 似乎无法正常工作 return。知道我在这里做错了什么吗?

当你遇到这样的问题,就让编译器来帮你吧。删除 (...) 重载以强制编译错误,看看 GCC 告诉你什么:

test.cc: In instantiation of ‘constexpr const bool has_enum_name<Foo>::value’:
test.cc:51:71:   required from here
test.cc:18:33: error: no matching function for call to ‘has_enum_name<Foo>::test(int)’
     !(std::is_same<decltype(test<T>(0)),std::false_type>::value);
                                 ^
test.cc:18:33: note: candidate is:
test.cc:12:36: note: template<class U> static decltype (std::enable_if<std::is_enum<typename U::name>::value, std::integral_constant<bool, true> >::type()) has_enum_name<T>::test(int) [with U = U; T = Foo]
   template<typename U> static auto test(int) -> decltype(
                                    ^
test.cc:12:36: note:   template argument deduction/substitution failed:
test.cc: In substitution of ‘template<class U> static decltype (std::enable_if<std::is_enum<typename U::name>::value, std::integral_constant<bool, true> >::type()) has_enum_name<T>::test(int) [with U = Foo]’:
test.cc:18:33:   required from ‘constexpr const bool has_enum_name<Foo>::value’
test.cc:51:71:   required from here
test.cc:13:83: error: dependent-name ‘std::enable_if<std::is_enum<typename T::name>::value, std::integral_constant<bool, true> >::type’ is parsed as a non-type, but instantiation yields a type
     std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type() );
                                                                                   ^
test.cc:13:83: note: say ‘typename std::enable_if<std::is_enum<typename T::name>::value, std::integral_constant<bool, true> >::type’ if a type is meant

这正是问题所在:您只是忘记添加 typename。如果 type 是非类型,T::type() 可能有效:它可能只是一个函数调用。这就是编译器解析它的方式。

顺便说一句,decltype(typename T::type()) 似乎有点毫无意义,除非您专门尝试检查该类型是否可默认构造,而这不是您要在此处进行的。您可以直接使用 typename T::type,像这样:

template<typename U> static auto test(int) ->
  typename std::enable_if<std::is_enum<typename U::name>::value, std::true_type>::type;

如果您在没有 typename 的情况下尝试过,您会立即收到一条有用的错误消息。

你对 value 的初始化也不必要地复杂,因为你已经知道你正在处理 false_typetrue_type:它们都有一个 value 成员你可以使用。

static constexpr bool value = decltype(test<T>(0))::value;