【问题标题】:Selecting traits with enable_if - works with clang, but not with gcc使用 enable_if 选择特征 - 适用于 clang,但不适用于 gcc
【发布时间】:2014-11-26 09:43:49
【问题描述】:

我正在编写一段通用的 (C++11) 代码,它应该与 boost::multi_arrayEigen::Matrix 以及可能的其他类型的 n 维数组一起使用。在几个点上,我需要访问给定数组类型的元素类型。 boost 数组包含一个名为 Element 的 typedef,而 Eigen 数组包含一个名为 Scalar 的 typedef。

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

为此,我实现了 type-traits has_elementhas_scalar,它们确定相应的 typedef 是否存在。我的实现受到this blog post 关于类型要求的启发。

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

这个想法是,如果 typedef 不存在,编译器将选择 false_type 版本,如果 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;

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

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));
}

现在,奇怪的是这在 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>>> {

clang 版本是here 并且工作正常。 gcc版本为here,编译失败。


我的问题:我的代码是否正确,这是 GCC 4.8.1 的问题;还是我做错了什么,编译时 Clang 过于慷慨了?无论如何,如何更改我的代码以使 GCC 4.8.1 能够编译它?

【问题讨论】:

    标签: c++ templates c++11 gcc clang


    【解决方案1】:

    看起来是yet another manifestation 中的CWG issue 1558。该标准不清楚别名模板中未使用的模板参数是否会导致替换失败。 Clang 将其视为替换失败; GCC 只是忽略未使用的参数。

    使用

    template<typename... Ts>
    struct make_void { typedef void type;};
    
    template<typename... Ts>
    using ToVoid = typename make_void<Ts...>::type;
    

    改为you should see it compile in both compilers

    【讨论】:

    • 非常感谢。这解决了我的问题。
    猜你喜欢
    • 2022-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-20
    • 1970-01-01
    相关资源
    最近更新 更多