隐藏基于特征的类模板实例

时间:2018-03-29 15:22:17

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

我有一个类似下面的traits类,它反映了两种类型之间的兼容性:

template <typename ObjectType, typename ArgumentType>
struct Traits
{
    static const bool SpecialMethodAvailable = false;
};  

单个成员确定是否可以在类型为SpecialMethod()的参数类型为ObjectType的对象上调用ArgumentType

支持此功能的简单类如下:

class ClassWithSpecialMethod
{
public:
    template <typename T>
    void SpecialMethod(T param) { std::cout << "Special Method called with " << param << std::endl; }
};

template <typename ArgumentType>
struct Traits<ClassWithSpecialMethod, ArgumentType>
{
    static const bool SpecialMethodAvailable = true;
};

我想编写一个使用此traits类的worker类,并调用特殊方法(如果可用)。基本上类似于以下内容:

template <typename T>
struct Worker
{
    static void DoSomething(T t, GlobalDataType& globalData)
    {
        //if Traits<GlobalDataType, T>::SpecialMethodAvailable
        //    call the method
        //else
        //    do something different
    }
};

我尝试使用std::enable_if实现此目的。我的解决方案适用于Visual C 14.1编译器,但不适用于GCC。这是我试过的:

template <typename T, typename Enable = void>
struct Worker
{
    static void DoSomething(T t, GlobalDataType& globalData)
    {
        std::cout << "There is no special method (called with " << t << ")" << std::endl;
    }
};

template <typename T>
struct Worker<T, typename std::enable_if<Traits<GlobalDataType, T>::SpecialMethodAvailable>::type>
{
    static void DoSomething(T t, GlobalDataType& globalData)
    {
        globalData.SpecialMethod(t);
    }
};

我使用如下:

typedef ... GlobalDataType; //before the template declarations

int main()
{
    GlobalDataType td;

    int integer = 0;
    Worker<int>::DoSomething(integer, td);
}

如果GlobalDataType的typedef为ClassWithSpecialMethod,VS和GCC都可以正常编译并正确输出:

Special Method called with 0

但是,如果GlobalDataType的typedef被设置为不允许特殊方法(例如int),则VS仍会生成正确的输出,而GCC会导致编译错误:< / p>

  

在静态成员函数中'static void Worker :: SpecialMethodAvailable&gt; :: type&gt; :: DoSomething(T,GlobalDataType&amp;)':   source.cpp:38:15:错误:请求'globalData'中的成员'SpecialMethod',它是非类型的   GlobalDataType {aka int}'

有人可以解释为什么这不符合GCC的预期吗?什么是替代品?

Link to online compiler

2 个答案:

答案 0 :(得分:1)

Mvsc 14不执行模板所需的2阶段查找。

gcc(并且是正确的)。

globalData.SpecialMethod(t);对于t任何globalData.SpecialMethod都不正确,因此错误。 (template <typename T> struct Worker<T, std::enable_if_t<Traits<GlobalDataType, T>::SpecialMethodAvailable>> { template <typename G, typename U> static void f(G& g, U& u) { g.SpecialMethod(u); } static void DoSomething(T t, GlobalDataType& globalData) { f(globalData, t); } }; 不正确,不依赖于模板参数。)

通过后评估您可能拥有您想要的内容:

set_bit(Bin, N) ->
        << A:N/bits, _:1, B/bits >> = Bin,
        << A/bits, 1:1, B/bits >>.

Demo

答案 1 :(得分:1)

正如Jarod42所解释的,这种方法

static void DoSomething(T t, GlobalDataType& globalData)
{
    globalData.SpecialMethod(t);
}
GlobalDataType修正为int

错误(永久T类型),因为int确定没有SpecialMethod()

要通过最少的代码更改来解决此问题,您可以模板化第二个参数

template <typename U>
 static void DoSomething(T t, U & globalData)
  { globalData.SpecialMethod(t); }

如果您希望DoSomething()只接收GlobalDataType作为第二个参数,则只有当DoSomething为{{1}时,您才可以使用SFINAE强制启用U }}。

的东西
GlobalDataType
  

会有什么替代方案?

我建议你采用一种完全不同的方式(基于template <typename U> static typename std::enable_if<std::is_same<U, GlobalDataType>{}> DoSomething(T t, U & globalData) { globalData.SpecialMethod(t); } 示例),而不是函数声明。

首先,一些模板助手函数

std::declval()

现在你可以编写一个模板函数的声明,如果template <typename ObjectType, typename ... Args> constexpr auto withSpecialMethodHelper (int) -> decltype(std::declval<ObjectType>.SpecialMethod(std::declval<Args>...), std::true_type{} ); template <typename ... Args> constexpr std::false_type withSpecialMethodHelper (long); 有一个std::true_type可以使用类型为ObjectType的变量列表参数调用SpecialMethod(),则返回Args...

template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
   -> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );

或者更好,正如Jarod42建议的那样,通过using

template <typename ObjectType, typename ... Args>
using withSpecialMethod
   = decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );

如果您可以使用C ++ 14,还可以定义withSpecialMethod_v模板constexpr变量

template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
  = decltype(withSpecialMethod<ObjectType, Args...>())::value;

如果声明了函数或

template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
   = withSpecialMethod<ObjectType, Args...>::value;

如果是using,则可以简化使用。

现在Worker类和专业化成为

template <typename T, bool = withSpecialMethod_v<GlobalDataType, T>>
struct Worker
 {
    static void DoSomething (T t, GlobalDataType & globalData)
    {
        std::cout << "There is no special method (called with " << t << ")"
         << std::endl;
    }
 };

template <typename T>
struct Worker<T, true>
 {
   template <typename U>
    static void DoSomething(T t, U & globalData)
    { globalData.SpecialMethod(t); }
 };