SFINAE for specialization of traits on CRTP struct
I am tryng to specialize some traits (for instance std::is_arithmetic or assertion_traits in cppunit) for a template class (CRTP) that can hold a value of the template parameter type (something similar to BOOST_STRONG_TYPEDEF
)
I try to use SFINAE to restrict my specialization
the sample code works fine with gcc6 and upper, but does not compile with Visual c++ (2015 or 2017)
error C2753: 'is_ar<T>': partial specialization cannot match argument list for primary template
or clang6
error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
the sample code:
template<class T, typename B>
struct Base
{
using typed_type = T ;
using base_type = B ;
base_type x;
};
template <typename T>
struct MyType
{
using base_type = T;
base_type x;
};
struct CRTPInt : Base<CRTPInt, int> { };
template <typename T>
struct is_ar
{
static const bool value = false;
};
template <>
struct is_ar<int>
{
static const bool value = true;
};
template <class T, typename...>
using typer = T;
template <typename T>
struct is_ar<typer<T, typename T::base_type, typename T::typed_type>> : is_ar<typename T::base_type>
{
static const bool value = true;
};
static_assert(is_arithmetic<CRTPInt>::value, "failed");
static_assert(is_arithmetic<CRTPInt::base_type>::value, "failed");
What did i do wrong ?
Is it valid c++11 ? How can I make it work with Visual C++ compiler ?
the answer from max66 work fine assuming that I can modify the initial definition of the trait. but in practice i would like to specialize framework traits (cppunit::assertion_traits for instance) for the class created by the macro
#define MY_TYPEDEF(type , base) struct type: Base<type , base> { };
the classes declared by this macro are not template classes so I do not find a way to specialize for all the classes generated this way.
The only common denominator beeing the typenames base_type and typed_type being defined.
Any Idea ?
[meta.type.synop:1]
The behavior of a program that adds specializations for any of the templates defined in this subclause is undefined unless otherwise specified.
That subsection includes std::is_arithmetic
, so unfortunately you are not allowed to make this specialization at all.
The error from the compiler is saying the is_arithmetic
specialization isn't actually specializing the template. When the conditions are met, and removing the clutter, it reads
template<typename T>
struct is_arithmetic<T> : is_arithmetic<typename T::base_type> {};
To specialize, you may specialize it for your Base
template<typename T, typename B>
struct is_arithmetic<Base<T, B>> : is_arithmetic<B> {};
It is illegal to specialize is_arithmetic
Frankly, I don't know if is right g++ or clang++.
Anyway, I don't think is a good idea specialize a standard class is forbidden specialize std::arithmetic
(see Quentin's answer).
To avoid this sort of problems, I suggest you to define another (not-standard) type-traits, say isAr
, as follows
template <typename T, typename = T>
struct isAr : public std::is_arithmetic<T>
{ };
template <typename T>
struct isAr<T, typer<T, typename T::base_type, typename T::typed_type>>
: public isAr<typename T::base_type>
{ };
Now
static_assert(isAr<CRTPInt>::value, "failed");
static_assert(isAr<CRTPInt::base_type>::value, "failed");
compile with both g++ and clang++.
-- EDIT --
For your modified example, my suggestion become
template <typename T, typename = T>
struct isAr : public std::false_type
{ };
template <>
struct isAr<int> : public std::true_type
{ };
template <typename T>
struct isAr<T, typer<T, typename T::base_type, typename T::typed_type>>
: public isAr<typename T::base_type>
{ };
static_assert(isAr<CRTPInt>::value, "failed");
static_assert(isAr<CRTPInt::base_type>::value, "failed");
链接地址: http://www.djcxy.com/p/90836.html
上一篇: 从析构函数中抛出异常
下一篇: SFINAE用于CRTP结构特征的专业化