模板化双模板 class 方法

Templating a double-templated class method

这是我的简化代码:

template<typename device, template<typename device> class protocol>
class MyClass
{
public:
  template<typename select>
  bool method()
  {
    // Code
  }
};

我希望 methodprotocol 类型方面表现不同。

换句话说,我有两种不同的可能协议,我想根据这些协议为我的方法提供两种不同的行为。但是不知道怎么用模板写。

例如,使用 SFINAE(如果您接受 C++11 解决方案)

#include <iostream>
#include <type_traits>

template <typename>
struct protocol_1
 { };

template <typename>
struct protocol_2
 { };

template<typename device, template<typename> class protocol>
class MyClass
 {
   public:
      template<typename select, typename p = protocol<device>>
         typename std::enable_if<
         std::is_same<p, protocol_1<device>>::value, bool>::type
         method()
       { return true; }

      template<typename select, typename p = protocol<device>>
         typename std::enable_if<
         std::is_same<p, protocol_2<device>>::value, bool>::type
         method()
       { return false; }
 };

int main()
 {
   MyClass<int, protocol_1>  m1;
   MyClass<int, protocol_2>  m2;

   std::cout << m1.method<int>() << std::endl; // print 1 (true)
   std::cout << m2.method<void>() << std::endl; // print 0 (false)
 }

--- 编辑 ---

正如 Yakk 所指出的(谢谢!),这个解决方案很弱,因为使用了一个可以显式和规避的模板默认值。

一个例子;

MyClass<int, protocol_1>{}.method<void>(); 

被称为method()的"protocol_1"版本,使用p的默认值; but explciting p, 如下

MyClass<int, protocol_1>{}.method<void, protocol_2<int>>(); 

被称为 method() 的 "protocol_2" 版本,基于 protocol_1

MyClass 版本

为避免此问题,可以在 method() 的两个版本中添加 static_assert(),以检查并强制 p 等于其默认值(protocol<device>)

我的意思是...如下

  template<typename select, typename p = protocol<device>>
     typename std::enable_if<
        std::is_same<p, protocol_1<device>>::value, bool>::type
     method()
   {
     static_assert(std::is_same<p, protocol<device>>::value, "!");

     return true;
   }

  template<typename select, typename p = protocol<device>>
     typename std::enable_if<
     std::is_same<p, protocol_2<device>>::value, bool>::type
     method()
   {
     static_assert(std::is_same<p, protocol<device>>::value, "!");

     return false;
   }

所以

MyClass<int, protocol_1>{}.method<void, protocol_2<int>>();

生成编译器错误。

使用外部助手是另一种可能的解决方案:

template <typename T>
struct B {
};
template <template<typename> class protocol, typename select>
struct helper {
    static bool do_real() {
        return true;
    }
};
template <typename select>
struct helper<B, select> {
    static bool do_real() {
        return false;
    }
};
template<typename device, template<typename> class protocol>
class MyClass
{
public:
  template<typename select>
  bool method()
  {
    return helper<protocol, select>::do_real();
  }
};

喜欢this

您可以部分特化 class,例如:

template<typename device, template<typename> class protocol>
class MyClass;

template <typename> struct protocole1 { /* ... */ };
template <typename> struct protocole2 { /* ... */ };

template<typename device>
class MyClass<device, protocol1>
{
public:
  template<typename select>
  bool method()
  {
    // Code for protocole1
  }
};

template<typename device>
class MyClass<device, protocol2>
{
public:
  template<typename select>
  bool method()
  {
    // Code for protocole2
  }
};

你没有说哪个是目标标准,我尽量给你一个四季皆宜的替代方案。


在 C++11/14 中,您可以使用标记分派和函数重载来执行此操作。
它遵循一个最小的工作示例:

#include<iostream>

template<typename> struct protocol_a {};
template<typename> struct protocol_b {};

template<typename device, template<typename> class protocol>
class MyClass {
    template<template<typename> class> struct tag {};

    template<typename select>
    bool method(tag<protocol_a>) {
        std::cout << "protocol_a" << std::endl;
        return true;
    }

    template<typename select>
    bool method(tag<protocol_b>) {
        std::cout << "protocol_b" << std::endl;
        return false;
    }

public:
    template<typename select>
    bool method() {
        return method<device>(tag<protocol>{});
    }
};

int main() {
    MyClass<int, protocol_a> mca;
    mca.method<void>();
    MyClass<int, protocol_b> mcb;
    mcb.method<void>();
}

非常紧凑。它不需要额外的 类、偏特化或 sfinae 表达式。 缺点(如果我们可以称其为缺点)是你多了一层间接。


在 C++17 中,您可以使用 if constexpr 获得相同的结果。
它遵循一个最小的工作示例:

#include<type_traits>
#include<iostream>

template<typename> struct protocol_a {};
template<typename> struct protocol_b {};

template<typename device, template<typename> class protocol>
class MyClass {
public:
    template<typename select>
    bool method() {
        if constexpr(std::is_same_v<protocol<device>, protocol_a<device>>) {
            std::cout << "protocol_a" << std::endl;
            return true;
        } else if constexpr(std::is_same_v<protocol<device>, protocol_b<device>>) {
            std::cout << "protocol_b" << std::endl;
            return false;
        }
    }
};

int main() {
    MyClass<int, protocol_a> mca;
    mca.method<void>();
    MyClass<int, protocol_b> mcb;
    mcb.method<void>();
}

更紧凑,但不能选择 C++17。
wandbox 上查看 运行。