问题 SFINAE没有发生std :: underlying_type


下面的SFINAE代码使用可变参数模板很好地编译使用clang 3.7.1,C ++ 14:

#include <array>
#include <iostream>
#include <vector>
#include <cstdint>

enum class Bar : uint8_t {
    ay, bee, see
};

struct S {

static void foo() {}

// std::begin(h) is defined for h of type H
template<typename H, typename... T>
static typename std::enable_if<std::is_pointer<decltype(std::begin(std::declval<H>()))*>::value>::type 
foo(const H&, T&&... t) 
{ std::cout << "container\n"; foo(std::forward<T>(t)...); }

// H is integral
template<typename H, typename... T>
static typename std::enable_if<std::is_integral<typename std::remove_reference<H>::type>::value>::type 
foo(const H&, T&&... t) 
{ std::cout << "integer\n"; foo(std::forward<T>(t)...); }

// H is an enum with underlying type = uint8_t
/*
template<typename H, typename... T>
static typename std::enable_if<std::is_same<typename std::underlying_type<H>::type,uint8_t>::value>::type 
foo(const H&, T&&... t)
{ std::cout << "enum\n"; foo(std::forward<T>(t)...); }
*/
};


int main()
{
    S::foo(std::array<int,8>(), 5, 5L, std::vector<int>{}, 5L);
}

我想要正确的重载 foo 根据类型递归调用 H

  1. 如果 std::begin(h) 定义为 h 类型 H, 我想要 要选择过载编号1
  2. 如果 H 是一个“整体类型”,我想要超载2号。

这样可以正常工作。但是,如果我添加另一个重载 枚举 类型(你可以尝试取消注释第三次重载),然后我得到:

错误:只有枚举类型具有基础类型

我同意这一点 枚举 有一个基础类型,因此为什么不是第三个重载(与 std::underlying_type)让SFINAE-d离开?


12107
2018-04-12 08:48


起源

你为什么不简单地使用 std::enable_if<std::is_enum<H>::value>::type?它 编译好。你有具体的 enums与基础类型 uint8_t 只要?在这种情况下,您可以再添加1个条件 sizeof(H) == sizeof(uint8_t)。即.. std::is_enum<H>::value && (sizeof(H) == sizeof(uint8_t))。这在上面的ideone示例中有所涉及。 - iammilind
@iammilind:好的建议,谢谢 - Paolo M


答案:


std::underlying_type 不是SFINAE友好的。试图访问 std::underlying_type<T>::type 对于非枚举类型,会导致未定义的行为(通常是硬错误),而不是替换失败。

在尝试访问其基础类型之前,您需要首先确定所讨论的类型是枚举类型。在线写这个就行了 typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type。替换 typename std::underlying_type<H>::type 在你的返回类型与这个可怕的混乱,你得到一个更可怕的混乱,工作:)

如果你发现自己经常需要这样做 - 或者只是不想写 typename std::enable_if<std::is_same<typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type, uint8_t>::value>::type  - 你可以写一个SFINAE友好的 underlying_type

template<class T, bool = std::is_enum<T>::value>
struct safe_underlying_type : std::underlying_type<T> {};
template<class T>
struct safe_underlying_type<T, false /* is_enum */> {};

14
2018-04-12 09:07