enable_if 和多个条件的问题

Issue with enable_if and multiple conditions

我试图实现一个将泛型类型转换为字符串的函数。需要使用 std::to_string() 将整数类型、使用 std::string() 的字符串和字符以及使用其他方法之一逐个元素的向量转换为字符串(取决于它们的内容)。

这是我的:

//Arithmetic types    

template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type convertToString(const T& t){
    return std::to_string(t);
}

//Other types using string ctor

template<class T>
typename std::enable_if<std::__and_<std::__not_<std::is_arithmetic<T>>::type,
        std::__not_<std::is_same<T, <T,
       std::vector<typename T::value_type, typename T::allocator_type>>::value
       >>>::value, std::string>::type convertToString(const T& t){
    return std::string(t);
}

//Vectors

template<class T>
typename std::enable_if<std::is_same<T, std::vector<typename T::value_type, 
   typename T::allocator_type>>::value, std::string>::type convertToString(const T& t){
    std::string str;
    for(std::size_t i = 0; i < t.size(); i++){
        str += convertToString(t[i]);
    }
    return str;
}

问题是第二个函数没有编译。我如何设计第二个函数,使其能够编译(和工作)并且不会产生歧义问题?

一种可能的方法是添加 is_vector 特征(查看 here 了解更多详情):

template<typename T> struct is_vector : public std::false_type {};

template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};

然后修改你的convertToString函数模板如下:

// Arithmetic types

template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type convertToString(const T& t) {
    return std::to_string(t);
}

// Other types using string ctor

template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value && !is_vector<T>::value, std::string>::type convertToString(const T& t) {
    return std::string(t);
}

// Vectors

template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value && is_vector<T>::value, std::string>::type convertToString(const T& t) {
    std::string str;
    for(std::size_t i = 0; i < t.size(); i++){
        str += convertToString(t[i]);
    }
    return str;
}

wandbox example

标有错误的模板:

template<class T>
typename std::enable_if<std::__and_<std::__not_<std::is_arithmetic<T>>::type,
//                                                                    ^^^^^^[1]
        std::__not_<std::is_same<T, <T,
//                                  ^^^[2]
       std::vector<typename T::value_type, typename T::allocator_type>>::value
//                                                                     ^^^^^^^[3]
       >>>::value, std::string>::type convertToString(const T& t){
//       ^[4]
    return std::string(t);
}
// [1] nested ::type not needed and ill-formed without typename keyword
// [2] <T, is garbage
// [3] nested ::value ill-formed because std::__not_ argument must be a type
// [4] too many closing angle brackets

修复错误的模板:

template<class T>
typename std::enable_if<std::__and_<std::__not_<std::is_arithmetic<T>>,
        std::__not_<std::is_same<T,
       std::vector<typename T::value_type, typename T::allocator_type>>
       >>::value, std::string>::type convertToString(const T& t){
    return std::string(t);
}

answer explains why your type trait doesn't compile. Also, you shouldn't use __and_ and __not_. Those are reserved and could easily change in the next compiler version. It's easy enough to implement your own version of those traits (e.g. see the possible implementation of conjunction)。

我建议采用完全不同的方法。我们可以使用 choice<> 使这些情况的重载变得简单得多:

template <int I> struct choice : choice<I+1> { };
template <> struct choice<10> { };

通过:

// arithmetic version
template <class T>
auto convertToStringHelper(T const& t, choice<0> )
    -> decltype(std::to_string(t))
{
    return std::to_string(t);
}

// non-arithmetic version
template <class T>
auto convertToStringHelper(T const& t, choice<1> )
    -> decltype(std::string(t))
{
    return std::string(t);
}

// vector version
template <class T, class A>
std::string convertToStringHelper(std::vector<T,A> const& v, choice<2> )
{
    // implementation here
}

template <class T>
std::string convertToString(T const& t) {
    return convertToStringHelper(t, choice<0>{});
}

这很好,因为您可以获得所有 SFINAE,而没有任何 enable_if 垃圾。