C++中的静态鸭子键入
Static duck typing in C++
C++对模板参数给出的类型具有某种鸭子类型。我们不知道DUCK1
和DUCK2
会是什么类型,但只要它们能quack()
,它就会编译并运行:
template <class DUCK1, class DUCK2>
void let_them_quack(DUCK1* donald, DUCK2* daisy){
donald->quack();
daisy->quack();
}
但是写起来有点不方便。当我完全不在乎DUCK1
和DUCK2
的实际类型是什么,而是想充分利用鸭子打字的想法时,那么我希望有一些与上面略有不同的东西:
- 我想省略编写重复且几乎毫无意义的模板参数列表(想象一下如果有 7 只鸭子会发生什么......
- 我想更明确一点,这些类型永远不会被使用,只有接口才是重要的。
- 我想要一个界面注释/检查。以某种方式明确类型背后的接口。(然而,这与鸭子打字有点对比。
C++是否提供任何功能来实现 3 个想法中的一个或多个?
(我知道虚拟继承是在大多数情况下实现此类模式的首选方法,但这里的问题专门涉及静态多态性的情况。
关于问题 1 和 2:从 C++14 开始,您可以省略显式template <typename ...
样板并使用 auto
,但只能在 lambda 中:
auto let_them_quack = [] (auto & donald, auto & daisy){
donald.quack();
daisy.quack();
};
(是的,我更喜欢引用指针)。GCC 允许在通常的功能中作为扩展这样做。
对于问题3,您所说的称为概念。它们在C++中存在了很长时间,但仅作为文档术语。现在概念 TS 正在进行中,允许您编写类似的东西
template<typename T>
concept bool Quackable = requires(T a) {
a.quack();
};
void let_them_quack (Quackable & donald, Quackable & daisy);
请注意,它尚未C++,只是一个技术规范正在进行中。不过,GCC 6.1似乎已经支持它。可以使用当前C++实现概念和约束;你可以在Boost中找到一个。
我想省略编写重复的模板参数列表 而且大多毫无意义(想象一下如果有 7 个会发生什么 鸭子...
为此,您可以使用可变参数模板并执行以下操作:
template<typename DUCK>
void let_them_quack(DUCK &&d) {
d.quack();
}
template<typename DUCK, typename... Args>
void let_them_quack(DUCK &&d, Args&& ...args) {
d.quack();
let_them_quack(std::forward<Args>(args)...);
}
现场演示
#2 和 #3 得到了一些照顾,因为如果给定的类没有实现接口,代码将无法编译并抛出编译错误。您也可以将其正式化:
class duck {
public:
virtual void quack()=0;
};
然后将函数的参数声明为获取指向鸭子的指针。您的类必须继承自此类,使let_them_quack()
的要求非常清晰。
就#1而言,可变参数模板可以解决这个问题。
void let_them_quack()
{
}
template <typename ...Args>
void let_them_quack(duck* first_duck, Args && ...args) {
first_duck->quack();
let_them_quack(std::forward<Args>(args)...);
}
您将能够使它看起来更具有概念(尚未达到标准 - 但非常接近):
http://melpon.org/wandbox/permlink/Vjy2U6BPbsTuSK3u
#include <iostream>
template<typename T>concept bool ItQuacks(){
return requires (T a) {
{ a.quack() } -> void;
};
}
void let_them_quack2(ItQuacks* donald, ItQuacks* daisy){
donald->quack();
daisy->quack();
}
struct DisneyDuck {
void quack(){ std::cout << "Quack!";}
};
struct RegularDuck {
void quack(){ std::cout << "Quack2!";}
};
struct Wolf {
void woof(){ std::cout << "Woof!";}
};
int main() {
DisneyDuck q1, q2;
let_them_quack2(&q1, &q2);
RegularDuck q3, q4;
let_them_quack2(&q3, &q4);
//Wolf w1, w2;
//let_them_quack2(&w1, &w2); // ERROR: constraints not satisfied
}
输出:
Quack!Quack!Quack2!Quack2!
如您所见,您将能够: omit writing a template parameter list
,ItQuacks 非常明确,因此types are never used and that it's only the interface that matters
发生。这个I'd like to have sort of an interface annotation/check.
也会发生,概念的使用也会给你有意义的错误消息。
我们只需要编写一个版本的函数:
#include <utility>
template<typename... Quackers>
void let_them_quack(Quackers&& ...quackers) {
using expand = int[];
void(expand { 0, (std::forward<Quackers>(quackers).quack(), 0)... });
}
struct Duck {
void quack() {}
};
int main()
{
Duck a, b, c;
let_them_quack(a, b, c, Duck());
}
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- #为""定义宏;静态";针对不同的上下文
- cmake如何在fedora工作站中找到boost静态库包
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 将公共但非静态的成员函数与ALGLIB集成
- cmake在我的项目中所需的所有静态库都不成功
- C++从另一个类访问公共静态向量的正确方法是什么
- 基于boost的程序的静态链接——zlib问题
- 在静态库中嵌入类方法
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 如何在C++中获得"静态纯虚拟"功能?
- 私有类型的静态常量成员
- 使用gcc从静态链接的文件中查找可选符号
- 在 .h 文件中的类中声明静态变量和在.cpp文件中声明"global"变量有什么区别
- 如何在C++中使用非静态成员函数作为回调函数
- 将静态库链接到不带-fPIC的共享库中
- 静态结构和一个定义规则
- 为什么虚函数不能是静态的和全局的?
- C++17中函数模板中的静态数组初始化(MSVC 2019)
- C++中的静态鸭子键入