如果可用,请使用模板参数的实现,否则为默认值
Use template argument's implementation if available, default otherwise
我想为不同的Controller
创建一个Frontend
模板类。如果方法可用(即支持某个功能),Frontend
应该使用Controller
的方法实现,否则应该使用默认方法,如
template <typename Controller>
class Frontend
{
public:
something()
{
// use Controller::something() if possible
// else use default implementation
}
};
- CCD_ 5将在内部使用类型特征来了解更多关于CCD_
Controller
不需要从任何提供默认实现的基类派生,因为默认方法实现将需要Frontend
专用的信息
将有大约20种方法可以在Controller
中实现。我试图创建(嵌套的)特征,提供有关所提供实现的信息:
// (T: Controller class)
T::supportsFeature<FeatureClass, ...>::type
因此,必须提供Controller::supportsFeature<>
来通知具体的实现。如果控制器支持某个功能,则::type
为std::true_type
,如果不支持,则为std::false_type
。为此,我创建了一个默认结构,该结构禁用任何功能,因此Controller类必须显式启用它提供的任何功能。这似乎是一种将信息从Controller
传送到Frontend
的舒适方式,但它有两个主要缺点:
其他人(最终将提供
Controller
)很难实现supportsFeature
,因为嵌套traits类的专门化必须编写我不确定我应该如何回避
Frontend::something()
中某个特征的存在,因为没有任何论点取决于该特征的存在——我无法提供一个合理表达的重载(我不想重载std::true_type
与std::false_type
,因为这本身就不说明问题)。我可以简单地使用if语句,并依靠编译器来删除死代码。我应该吗?
所以,总结一下:
- 如何将有关方法存在的信息从模板类参数传递到模板类
- 如何根据这些信息在实现之间正确切换
我用它来检查确切的签名:
#include <cstdint>
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)
template <typename U>
class traitsName
{
private:
template<typename T, T> struct helper;
template<typename T>
static std::uint8_t check(helper<signature, &funcName>*);
template<typename T> static std::uint16_t check(...);
public:
static
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
}
DEFINE_HAS_SIGNATURE(has_something, T::something, void (T::*)());
然后使用SFINAE(类似):
template <typename Controller>
class Frontend
{
public:
void something()
{
somethingT<Controller>();
}
private:
template <typename T>
typename std::enable_if<has_something<T>::value>::type
somethingT()
{
controller.something();
}
template <typename T>
typename std::enable_if<!has_something<T>::value>::type
somethingT()
{
// default implementation
}
};
或标签调度(类似):
template <typename Controller>
class Frontend
{
public:
void something()
{
something(typename std::conditional<has_something<Controller>::value,
std::true_type,
std::false_type>::type());
}
private:
void something(std::true_type) { controller.something(); }
void something(std::false_type) { /* default implementation */ }
};
经过一些尝试/错误后,以下是我所称的可接受的解决方案。
当然,使用SFINAE,关于是使用Controller的成员函数还是默认函数的所有推导都是在编译时完成的。
Controllers类型的实现者唯一需要的就是将控制器中的类型定义为typedef Controller WithSomething;
。与你提到的特质相比,这并不难。
首先声明Frontend
模板类,并为20个可调用函数中的每一个定义两个模板函数。这里只有两个foo
和bar
。
#include <iostream>
using std::cout;
template <typename Ctrl>
class Frontend;
template <typename Ctrl>
void call_foo( typename Ctrl::WithFoo & ctrl ) { ctrl.foo(); }
template <typename Ctrl>
void call_foo( Ctrl & ctrl ) { Frontend<Ctrl>::default_foo( ctrl ); }
template <typename Ctrl>
void call_bar( typename Ctrl::WithBar & ctrl ) { ctrl.bar(); }
template <typename Ctrl>
void call_bar( Ctrl & ctrl ) { Frontend<Ctrl>::default_bar( ctrl ); }
然后用可调用函数定义Frontend
函数。在这里,我将默认实现定义为静态成员,但这是可以更改的。
template <typename Ctrl>
class Frontend
{
public:
typedef Ctrl controller;
void foo() { call_foo<Ctrl>( c ); }
void bar() { call_bar<Ctrl>( c ); }
static void default_foo( Ctrl & ctrl ) { cout<<"Default foon"; }
static void default_bar( Ctrl & ctrl ) { cout<<"Default barn"; }
private:
Ctrl c;
};
最后给出了Controller
类的一些例子。一个同时定义foo
和bar
,另两个仅定义一个。
struct CtrlFooBar
{
typedef CtrlFooBar WithFoo;
typedef CtrlFooBar WithBar;
void foo() { cout<<"CtrlFB foon"; }
void bar() { cout<<"CtrlFB barn"; }
};
struct CtrlFoo
{
typedef CtrlFoo WithFoo;
void foo() { cout<<"CtrlFoo foon"; }
};
struct CtrlBar
{
typedef CtrlBar WithBar;
void bar() { cout<<"CtrlBar barn"; }
};
将Frondtend
与所有这些类一起使用,并将int
与预期一起使用。
int main()
{
Frontend<CtrlFooBar> c2;
Frontend<CtrlFoo> cf;
Frontend<CtrlBar> cb;
Frontend<int> ci;
c2.foo();
c2.bar();
cf.foo();
cf.bar();
cb.foo();
cb.bar();
ci.foo();
ci.bar();
return 0;
}
输出
CtrlFB foo
CtrlFB bar
CtrlFoo foo
Default bar
Default foo
CtrlBar bar
Default foo
Default bar
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 具有默认值的引用获取函数
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 当给定默认值时,为什么此模板参数推导失败
- 从具有默认值的部分指定模板类继承时发生SWIG错误,具有不带默认值的正向声明
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 格式化浮点值:返回默认值
- 如何将数组部分初始化为某个默认值?
- asn1c 不会从 asn.1 模块中提取八位字节字符串的默认值
- 创建一个包含 c++ 默认值的环境文件
- C++(和 ROS) - 包含与前向声明引用,设置默认值和类型定义
- Makefile g++ 使用命令行中的 -D 变量进行编译,默认值
- Switch 语句(字符串)一直选择默认值,除非其为零
- 如何使用默认值将枚举声明为 extern
- 如何在提升程序选项中设置矢量<矢量>的默认值<string>
- 如何使用默认值为构造函数中的枚举赋值?
- 变量始终在函数中重置为默认值
- 如何在C++中提供模板化函数作为另一个函数的参数,默认值?
- 如何使用选项真、假、默认值和切换C++实现标志
- 如果可用,请使用模板参数的实现,否则为默认值