6

I was wondering how we can declare an interface in C++ without using virtual functions. After some internet searching I put together this solution:

#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(){}

I'm planning to use this design strategy in a project where I will use static polymorphism only. The C++ standard I will use in the project is C++11.

What do you think are the pros and cons of this approach?

What improvements can be made on the code I proposed?

EDIT 1: I just realised that inheriting from Interface is not needed. This code could also be used:

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

EDIT 2-3: One major difference between the static_assert solution (with or without CRTP) and the standard CRTP is that the CRTP does not guarantee that the derived class implements all the interface members. E.g., the following code compiles correctly:

#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(){}

An error about a missing member function will be returned by the compiler only when the functions foo or bar will be required.

The way I see it, the static_assert solution feels more like an interface declaration than CRTP alone.

4

2 回答 2

3

实现静态多态的一种常用方法是使用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'
    } 
};

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

于 2017-06-01T21:34:46.737 回答
2

看来您想实现概念 (lite)。在尝试实施之前,您可能需要阅读这篇文章。

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

考虑Sortable链接中的示例。你可以创建一个类模板Sortablestatic_assert用来断言所有关于模板参数的想法。您向您的用户解释他们需要实现某些方法,并且为了强制实现该集合,他们需要使用Sortable<TheirClass>一种或另一种方法。

为了表达,就在函数声明中。您的功能需要 a 的想法,Sortable您将不得不求助于这样的东西:

template <typename Container> 
auto doSomethingWithSortable (Container&) -> std::enable_if<Implements<Container, Sortable>>::type;
于 2017-06-05T12:00:49.883 回答