模仿双模板类方法

时间:2016-12-28 16:54:32

标签: c++ c++11 templates template-templates

这是我的简化代码:

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

我希望methodprotocol类型方面采取不同的行动。

换句话说,我有两种不同的可能协议,我希望根据协议为我的方法提供两种不同的行为。但我不知道如何用模板写它。

4 个答案:

答案 0 :(得分:2)

你没有提到哪个是目标标准,所以我会尝试为你提供所有季节的替代品。

在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上运行。

答案 1 :(得分:1)

例如,使用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>(); 

被称为&#34; protocol_1&#34;版本method(),使用p的默认值;但请注明p,如下

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

被称为&#34; protocol_2&#34;基于method()

MyClass的{​​{1}}版本的protocol_1版本

要避免此问题,可以在static_assert()的两个版本中添加method(),以检查并强制p等于其默认值({{ 1}})

我的意思是......如下:

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

生成编译器错误。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

您可以对该课程进行部分专业化,例如:

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