I am working on a generic piece of (C++11) code that is supposed to work with boost::multi_array
, Eigen::Matrix
, and possibly other types of n-dimensional arrays. At several points I need access to the element-type of a given array-type. The boost arrays contain a typedef called Element
, while the Eigen arrays contain a typedef called Scalar
.
What I want is a type-trait that returns the element type of a given array type. Unfortunately, I cannot just template specialize the trait class for all possible array types, because Eigen uses expression-templates, and hence, there are infinitely many types of Eigen matrices. Therefore, I am using SFINAE with enable_if to implement my traits. The criterion which enable_if should select on is, whether a type has a typedef called Element
, or one called Scalar
.
For this to work I implemented type-traits has_element
, and has_scalar
, which determine whether the corresponding typedef exists. My implementation is inspired by this blog post about type-requirements.
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 {};
The idea is, that the compiler will pick the false_type
version if the typedef is not presented, and will pick the more specialized true_type
version if the typedef is present.
The actual trait to obtain the scalar type is implemented like this:
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;
A simple example use case is this:
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));
}
Now, the odd thing is that this works just fine with Clang 3.4. However, GCC 4.8.1 fails to compile this and gives the following error message:
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>>> {
The clang version is here and works fine. The gcc version is here and fails to compile.
My Question: Is my code correct, and this an issue with GCC 4.8.1; or am I doing something wrong and Clang is being too generous when compiling? In any case, how can I change my code such that GCC 4.8.1 would compile it?