标准::条件与标准::enable_if

std::conditional vs std::enable_if

本文关键字:标准 if 条件 enable      更新时间:2023-10-16

我有一个散列函数,可以接受任何对象类型并对其进行散列,它在内部使用std::hash。由于std::hash不支持枚举类型,因此我创建了函数的重载,1 用于使用 std::underlying_type 的枚举,1 用于其他类型的枚举:

template <typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
static std::size_t Hash(T const & t)
{
    return std::hash<typename std::underlying_type<T>::type>()(t);
}
template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
static std::size_t Hash(T const & t)
{
    return std::hash<T>()(t);
}

这工作正常。然后我尝试将其全部放入一个具有std::conditional的函数中:

template <typename T>
static std::size_t Hash(T const & t)
{
    typedef typename std::conditional<std::is_enum<T>::value, std::hash<typename std::underlying_type<T>::type>, std::hash<T>>::type Hasher;
    return Hasher()(t);
}

主要:

enum test
{
    TEST = 2
};
int main() {
    Hash<test>(TEST);
    Hash<int>(5);
    std::cin.get();
    return 0;
}

但是,这给了我一个错误:

/

usr/include/c++/5/type_traits:2190:38:错误:"int"不是枚举类型 类型定义 __underlying_type(_Tp) 类型;

我确实理解错误,但我不明白为什么,我认为std::conditional可以防止这些编译时错误,因为<int>在编译时使用std::hash<T>而不是std::hash<typename std::underlying_type<T>::type>

我在这里做错了什么,有没有办法合并这两个功能?

问题是,

如果模板参数不是枚举,则std::underlying_type的格式不正确,并且std::conditional的两个分支都需要有效。

一种可能性是创建一个safe_underlying_type特征,如果T不是枚举,则只返回void

template <typename T, typename = typename std::is_enum<T>::type>
struct safe_underlying_type {
    using type = void;
};
template <typename T>
struct safe_underlying_type<T, std::true_type> {
    using type = std::underlying_type_t<T>; 
};

或者你可以只写一个特征来获取你想要的哈希类型:

template <typename T, typename = typename std::is_enum<T>::type>
struct hash_type {
    using type = std::hash<T>;
};
template <typename T>
struct hash_type<T, std::true_type> {
    using type = std::hash<std::underlying_type_t<T>>;  
};

如果你真的想使用 conditional ,你需要推迟评估:

template<class T> struct identity { using type = T; };
using Hasher = std::hash<typename std::conditional<std::is_enum<T>::value,
                                                   std::underlying_type<T>,
                                                   identity<T>>::type::type>;

此外,自 LWG 2148 以来,std::hash应该原生支持枚举。

typename std::underlying_type<T>::type

对于int来说,它的格式并不好,但它在编译时仍然被计算。这就是您收到此错误的原因。您已经使用 std::enable_if 给出了解决方案,所以我不会在那里详细介绍。我认为这很好,无论如何使用std::conditional只会使阅读变得更加困难。

conditional中使用的两种类型(分支)在编译时都需要格式正确。

问题是,当编译器解析std::conditional本身时,语句typename std::underlying_type<T>::type需要格式正确(并且不适用于int)。结果还是什么并不重要,它还没有走那么远。