c++ - SFINAE 没有发生在 std::underlying_type 上

标签 c++ enums variadic-templates sfinae overload-resolution

下面带有可变参数模板的 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);
}

我希望根据 H 类型递归调用正确的 foo 重载:

  1. 如果 std::begin(h) 是为 H 类型的 h 定义的,我想要 选择 1 号过载
  2. 如果 H 是“整数类型”,我想要重载号 2。

按原样工作。但是,如果我为 enum 类型添加另一个重载(您可以尝试取消注释第三个重载),那么我得到:

error: only enumeration types have underlying types

我同意只有 enums 有一个底层类型,因此为什么不是第三个重载(带有 std::underlying_type)让 SFINAE-d 消失?

最佳答案

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 */> {};

关于c++ - SFINAE 没有发生在 std::underlying_type 上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36568050/

相关文章:

c++ - 如何反转可变参数模板函数的参数顺序?

c++ - 可变递归模板 mem 有趣的特化

c++ - 是否可以将作用域枚举 ("enum class") 上下文转换为 bool 值?

任何枚举类型的 C++11 哈希函数

c# - 类型为枚举的变量如何获取超出其元素范围的值?

c++ - 如何从参数包构造引用的 std::tuple?

c++ - 什么时候应该使用直接初始化,什么时候应该使用复制初始化?

c++ - 为什么我会收到编译器错误,尽管大小是常量?

c++ - 使用 mingw730 32 位的 Qt 5.12 项目发布构建中代码位置的偏移量

c++ - 我的类的可变参数模板构造函数不能修改我的类成员,为什么会这样?