c++ - 根据特征隐藏类模板实例

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

我有一个如下所示的特征类,它反射(reflect)了两种类型之间的兼容性:

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

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

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

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

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

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 类型定义为 ClassWithSpecialMethod,则 VS 和 GCC 都可以正常编译并正确输出:

Special Method called with 0

但是,如果将GlobalDataType类型定义为不允许特殊方法的内容(例如int),VS仍然会产生正确的输出,而GCC会产生编译错误:

In static member function ‘static void Worker::SpecialMethodAvailable>::type>::DoSomething(T, GlobalDataType&)’: source.cpp:38:15: error: request for member ‘SpecialMethod’ in ‘globalData’, which is of non-class type GlobalDataType {aka int}’

有人可以解释为什么这在 GCC 下不能按预期工作吗?有什么替代方案?

Link to online compiler

最佳答案

正如 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,您可以使用 SFINAE 强制启用 DoSomething,仅当 UGlobalDataType 时。诸如此类

template <typename U>
 static typename std::enable_if<std::is_same<U, GlobalDataType>{}>
   DoSomething(T t, U & globalData)
 { globalData.SpecialMethod(t); }

What would be alternatives?

我建议您采用一种完全不同的方式,基于函数声明(遵循 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);

现在,如果 ObjectType 有一个 SpecialMethod() ,您可以编写返回 std::true_type 的模板函数声明,该函数可以使用 Args...

类型的参数的可变参数列表进行调用
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
   -> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );

或者也许更好,正如 Jarod42 所建议的那样,通过使用

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;

如果使用,可以简化使用。

现在,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); }
 };

关于c++ - 根据特征隐藏类模板实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49559750/

相关文章:

c++ - 尝试使用未知类型的模板非类型参数

c++ - 模板类对象的比较器,C++

c++ - 用位板识别棋子

c++ - 将类更改为静态类有些奇怪

c++ - 整理私有(private)函数的元编程技巧

c++ - 模板结构的静态常量成员的不同值

c++ - 多线程程序导致段错误,使用 std::list::push_back

c++ - 在需要 ifstream 的地方传入 fstream

c++ - 总结两个 boost::accumulator_set 实例

c++ - 如何存储或转发 CRTP 模板类的类型