【问题标题】:SFINAE for specialization of traits on CRTP structSFINAE 用于 CRTP 结构上的特征特化
【发布时间】:2017-11-22 16:15:40
【问题描述】:

我正在尝试为模板类 (CRTP) 专门化一些特征(例如 cppunit 中的 std::is_arithmetic 或 assertion_traits),它可以保存模板参数类型的值(类似于BOOST_STRONG_TYPEDEF

我尝试使用 SFINAE 来限制我的专业化

示例代码适用于 gcc6 及更高版本,但无法使用 Visual c++(2015 或 2017)进行编译

error C2753: 'is_ar<T>': partial specialization cannot match argument list for primary template

或clang6

error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list

示例代码:

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

我做错了什么?

它是有效的 c++11 吗? 如何使它与 Visual C++ 编译器一起工作?

假设我可以修改特征的初始定义,来自 max66 的答案可以正常工作。但在实践中,我想为宏创建的类专门化框架特征(例如 cppunit::assertion_traits)

#define MY_TYPEDEF(type , base) struct type: Base<type , base> { };

这个宏声明的类不是模板类,所以我没有找到一种方法来专门处理以这种方式生成的所有类。

唯一的共同点是类型名 base_typetyped_type 被定义。

有什么想法吗?

【问题讨论】:

    标签: c++ c++11 template-specialization sfinae crtp


    【解决方案1】:

    [meta.type.synop:1]
    除非另有说明,否则为本小节中定义的任何模板添加特化的程序的行为是未定义的。

    该小节包括std::is_arithmetic,因此很遗憾,您根本不允许进行此专业化。

    【讨论】:

      【解决方案2】:

      编译器的错误是说is_arithmetic 特化实际上并没有特化模板。当满足条件并去除杂乱时,它会读取

      template<typename T>
      struct is_arithmetic<T> : is_arithmetic<typename T::base_type> {};
      

      要专精,你可以专为你的Base

      template<typename T, typename B>
      struct is_arithmetic<Base<T, B>> : is_arithmetic<B> {};
      

      It is illegal to specialize is_arithmetic

      【讨论】:

        【解决方案3】:

        坦率地说,我不知道是 g++ 还是 clang++。

        无论如何,我不认为专门化一个标准类是个好主意禁止专门化std::arithmetic(参见Quentin's answer)。

        为避免此类问题,我建议您定义另一个(非标准)类型特征,例如isAr,如下所示

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

        现在

        static_assert(isAr<CRTPInt>::value, "failed");
        static_assert(isAr<CRTPInt::base_type>::value, "failed");
        

        同时使用 g++ 和 clang++ 编译。

        -- 编辑--

        对于您修改后的示例,我的建议变为

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

        【讨论】:

        • 我的问题不仅在于 is_arithmetic,还在于不同的特征。我更新了没有代码的示例,但它仍然无法使用 Clang 或 Visual C++ 编译 f
        • @yvanherreros - 因为仍然存在问题,根据 clang++ 和 Visual c++,typer 的专业化并不比通用版本更专业化;我不知道是正确的 g++(认为专业化更专业)还是 clang++/visual c++,但我展示了一种解决所有编译器问题的方法:添加默认模板类型并在第二个中使用 typer类型。根据您的新示例修改了我的答案。
        • 工作正常。非常感谢。 !在第一次阅读您之前的答案时,我没有抓住第二个模板参数的技巧。
        猜你喜欢
        • 1970-01-01
        • 2019-07-18
        • 1970-01-01
        • 1970-01-01
        • 2017-06-24
        • 2013-12-21
        • 2023-03-18
        • 2018-02-16
        • 1970-01-01
        相关资源
        最近更新 更多