没有虚函数的C ++接口

时间:2017-06-01 21:22:28

标签: c++ interface static-polymorphism

我想知道如何在不使用虚函数的情况下在C ++中声明接口。经过一些互联网搜索,我把这个解决方案放在一起:

#include <type_traits>

using namespace std;

// Definition of a type trait to check if a class defines a member function "bool foo(bool)"
template<typename T, typename = void>
struct has_foo : false_type { };

template<typename T>
struct has_foo<T, typename enable_if<is_same<bool, decltype(std::declval<T>().foo(bool()))>::value, void>::type> : true_type { };

// Definition of a type trait to check if a class defines a member function "void bar()"    
template<typename T, typename = void>
struct has_bar : false_type { };

template<typename T>
struct has_bar<T, typename enable_if<is_same<void, decltype(std::declval<T>().bar())>::value, void>::type> : true_type { };

// Class defining the interface
template <typename T>
class Interface{
public:
  Interface(){
    static_assert(has_foo<T>::value == true, "member function foo not implemented");
    static_assert(has_bar<T>::value == true, "member function bar not implemented");
  }
};

// Interface implementation
class Implementation:Interface<Implementation>{
public:
  // If the following member functions are not declared a compilation error is returned by the compiler
  bool foo(bool in){return !in;}
  void bar(){}
};

int main(){}

我计划在一个只使用静态多态的项目中使用此设计策略。 我将在项目中使用的C ++标准是C ++ 11。

您认为这种方法的优缺点是什么?

我可以对我提出的代码做出哪些改进?

编辑1: 我刚刚意识到不需要从Interface继承。也可以使用此代码:

class Implementation{
  Interface<Implementation> unused;
public:
  bool foo(bool in){return !in;}
  void bar(){}
};

编辑2-3: static_assert解决方案(有或没有CRTP)和标准CRTP之间的一个主要区别是CRTP不保证派生类实现所有接口成员。例如,以下代码正确编译:

#include <type_traits>
using namespace std;

template< typename T>
class Interface{
public:
  bool foo(bool in){
    return static_cast<T*>(this)->foo(in);
  }
  void bar(){
    static_cast<T*>(this)->bar();
  }
};

class Implementation: public Interface<Implementation>{
public:
//    bool foo(bool in){return !in;}
//    void bar(){}
};

int main(){}

仅当需要函数 foo bar 时,编译器才会返回有关缺少成员函数的错误。

我看到它的方式,static_assert解决方案感觉更像是一个接口声明,而不仅仅是CRTP。

2 个答案:

答案 0 :(得分:1)

实现静态多态的一种常用方法是使用CRTP

使用此模式,您可以定义模板化接口类,其方法转发到模板:

// Interface
template <typename T>
struct base {
    void foo(int arg) {
        static_cast<T*>(this)->do_foo(arg);
    }
};

您从基类实现继承并实现方法:

// Implementation
struct derived : base<derived> {
    void do_foo(int arg) {
        std::cout << arg << '\n'
    } 
};

这种模式的优势在于它看起来“感觉”很像常规的运行时多态性,并且错误消息通常非常清晰。因为编译器可以看到所有代码,所以所有内容都可以内联,因此没有开销。

答案 1 :(得分:1)

您似乎想要实施concepts (lite)。您可能希望在尝试实施之前阅读该文章。

如果没有编译器支持,您可以部分实现此想法。您的static_assert想法是表达界面要求的已知方式。

考虑链接中的Sortable示例。您可以创建一个类模板Sortable,使用static_assert来断言对模板参数的所有想法。您向用户解释他们需要实现某些方法,并强制执行该方法,他们需要以某种方式使用Sortable<TheirClass>

为了表达,在函数声明中。你的函数需要Sortable的想法,你将不得不求助于这样的事情:

template <typename Container> 
auto doSomethingWithSortable (Container&) -> std::enable_if<Implements<Container, Sortable>>::type;