c++ - 使用 enable_if 选择特征 - 适用于 clang,但不适用于 gcc

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

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

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

为此,我实现了类型特征 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 版本,并将选择更专业的 true_type如果存在 typedef,则为版本。

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

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 能够编译它?

最佳答案

看起来是 yet another manifestationCWG 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 .

关于c++ - 使用 enable_if 选择特征 - 适用于 clang,但不适用于 gcc,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26143259/

相关文章:

c++ - Tesseract 与 XCode 一起工作

c++ - 带有模板类模板参数的模板类的变量设置为带有变量的派生模板的基本模板

c++ - 如何为模板类的成员函数定义静态数组?

c++ - 在 C++11 中通过 const 迭代器进行销毁

c++ - std::set 使用 char * 类型查找行为

c++ - 在我的 C++ 程序中使用 OpenStreetMap

c++ - 如何使用代码在 QT 中设置标签样式

c++按值传递模板化unique_ptr

python - 与 Python 中的 string.Template 相反

c++11 vs c++ - 枚举差异