没有虚函数的 C++ 接口
C++ interface without virtual functions
我想知道我们如何在不使用虚函数的情况下在 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 不保证派生的 class 实现所有接口成员。例如,以下代码可以正确编译:
#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。
实现静态多态的一种常用方法是使用CRTP.
使用此模式,您可以定义一个模板化接口 class,其方法转发到模板:
// Interface
template <typename T>
struct base {
void foo(int arg) {
static_cast<T*>(this)->do_foo(arg);
}
};
您实现了继承自基础 class 并实现了以下方法:
// Implementation
struct derived : base<derived> {
void do_foo(int arg) {
std::cout << arg << '\n'
}
};
这种模式的优点是它看起来 "feels" 很像常规的运行时多态性,而且错误消息通常非常正常。因为所有代码对编译器都是可见的,所以所有内容都可以内联,因此没有开销。
您似乎想要实施 concepts (lite)。在尝试实施之前,您可能需要阅读这篇文章。
没有编译器支持,你可以部分实现这个想法。您的 static_assert
想法是表达接口要求的一种已知方式。
考虑 link 中的 Sortable
示例。您可以创建一个 class 模板 Sortable
,使用 static_assert
断言关于模板参数的所有想法。您向您的用户解释他们需要实施某些方法,并且要强制实施该集合,他们需要以某种方式使用 Sortable<TheirClass>
。
为了表达,就在函数声明中吧。你的函数需要 Sortable
的想法,你将不得不求助于这样的东西:
template <typename Container>
auto doSomethingWithSortable (Container&) -> std::enable_if<Implements<Container, Sortable>>::type;
我想知道我们如何在不使用虚函数的情况下在 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 不保证派生的 class 实现所有接口成员。例如,以下代码可以正确编译:
#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。
实现静态多态的一种常用方法是使用CRTP.
使用此模式,您可以定义一个模板化接口 class,其方法转发到模板:
// Interface
template <typename T>
struct base {
void foo(int arg) {
static_cast<T*>(this)->do_foo(arg);
}
};
您实现了继承自基础 class 并实现了以下方法:
// Implementation
struct derived : base<derived> {
void do_foo(int arg) {
std::cout << arg << '\n'
}
};
这种模式的优点是它看起来 "feels" 很像常规的运行时多态性,而且错误消息通常非常正常。因为所有代码对编译器都是可见的,所以所有内容都可以内联,因此没有开销。
您似乎想要实施 concepts (lite)。在尝试实施之前,您可能需要阅读这篇文章。
没有编译器支持,你可以部分实现这个想法。您的 static_assert
想法是表达接口要求的一种已知方式。
考虑 link 中的 Sortable
示例。您可以创建一个 class 模板 Sortable
,使用 static_assert
断言关于模板参数的所有想法。您向您的用户解释他们需要实施某些方法,并且要强制实施该集合,他们需要以某种方式使用 Sortable<TheirClass>
。
为了表达,就在函数声明中吧。你的函数需要 Sortable
的想法,你将不得不求助于这样的东西:
template <typename Container>
auto doSomethingWithSortable (Container&) -> std::enable_if<Implements<Container, Sortable>>::type;