使用enable_if选择特征 - 与clang一起使用,但不与gcc一起使用

Lem*_*ing 4 c++ gcc templates clang c++11

我的工作的通用件的(C++ 11)代码是应该一起工作boost::multi_array,Eigen::Matrix和n维阵列中的,可能是其它类型的.在几个点上,我需要访问给定数组类型的元素类型.boost数组包含一个名为的typedef Element,而Eigen数组包含一个名为的typedef Scalar.

我想要的是一个类型特征,它返回给定数组类型的元素类型.不幸的是,我不能只为所有可能的数组类型专门化trait类,因为Eigen使用表达式模板,因此,有无数种类型的特征矩阵.因此,我使用SFINAE和enable_if来实现我的特性.enable_if应该选择的标准是,类型是否具有被调用的typedef Element,还是被调用的类型Scalar.

为此,我实现了类型特征has_element,并has_scalar确定是否存在相应的typedef.我的实现受到关于类型要求的博客文章的启发.

template <class T>
using ToVoid = void;

template <class T, class Enable = void>
struct has_scalar : public std::false_type {};

template <class T>
struct has_scalar<T, ToVoid<typename T::Scalar>> : public std::true_type {};

template <class T, class Enable = void>
struct has_element : public std::false_type {};

template <class T>
struct has_element<T, ToVoid<typename T::Element>> : public std::true_type {};
Run Code Online (Sandbox Code Playgroud)

我们的想法是,false_type如果没有提供typedef ,编译器将选择版本,如果typedef存在,将选择更专业的 true_type版本.

获取标量类型的实际特征是这样实现的:

template <class Condition, class T = void>
using EnableIf = typename std::enable_if<Condition::value, T>::type;

template <class T, class Enable = void>
struct scalar_of;

template <class T>
struct scalar_of<T, EnableIf<has_element<T>>> {
    using type = typename T::Element;
};

template <class T>
struct scalar_of<T, EnableIf<has_scalar<T>>> {
    using type = typename T::Scalar;
};

template <class T>
using ScalarOf = typename scalar_of<T>::type;
Run Code Online (Sandbox Code Playgroud)

一个简单的用例是这样的:

struct BoostArray {
    using Element = double;
};

struct EigenArray {
    using Scalar = float;
};


int main() {
    using std::is_same;
    assert((is_same<double, ScalarOf<BoostArray>>::value));
    assert((is_same<float, ScalarOf<EigenArray>>::value));
}
Run Code Online (Sandbox Code Playgroud)

现在,奇怪的是,这对Clang 3.4来说效果很好.但是,GCC 4.8.1无法编译它并给出以下错误消息:

test.cpp: In substitution of ‘template<class T> using ScalarOf = typename scalar_of<T>::type [with T = BoostArray]’:
test.cpp:51:5:   required from here
test.cpp:37:45: error: ambiguous class template instantiation for ‘struct scalar_of<BoostArray, void>’
 using ScalarOf = typename scalar_of<T>::type;
                                             ^
test.cpp:27:8: error: candidates are: struct scalar_of<T, typename std::enable_if<has_element<T, void>::value, void>::type>
 struct scalar_of<T, EnableIf<has_element<T>>> {
        ^
test.cpp:32:8: error:                 struct scalar_of<T, typename std::enable_if<has_scalar<T, void>::value, void>::type>
 struct scalar_of<T, EnableIf<has_scalar<T>>> {
Run Code Online (Sandbox Code Playgroud)

铿锵版在这里工作正常.gcc版本在这里,无法编译.


我的问题:我的代码是否正确,这是GCC 4.8.1的问题; 或者我做错了什么,Clang在编译时太慷慨了?在任何情况下,我如何更改我的代码,以便GCC 4.8.1编译它?

T.C*_*.C. 10

看起来这是CWG问题1558的另一个表现形式.该标准尚不清楚别名模板中未使用的模板参数是否会导致替换失败.Clang将其视为替代失败; GCC只是忽略了未使用的参数.

使用

template<typename... Ts>
struct make_void { typedef void type;};

template<typename... Ts>
using ToVoid = typename make_void<Ts...>::type;
Run Code Online (Sandbox Code Playgroud)

相反,你应该看到它在两个编译器中编译.