我想为不同的Frontend
创建一个Controller
模板类。 Frontend
应该使用Controller
方法的实现(如果可用)(即支持某个功能),否则应使用默认值,如
template <typename Controller>
class Frontend
{
public:
something()
{
// use Controller::something() if possible
// else use default implementation
}
};
Frontend
将在内部使用类型特征,以了解有关Controller
,Controller
不需要从任何提供默认实现的基类派生,因为默认方法实现需要私有Frontend
的信息。 Controller
中可能会实施约20种方法。我试图创建(嵌套)特征,提供有关提供的实现的信息:
// (T: Controller class)
T::supportsFeature<FeatureClass, ...>::type
因此必须提供Controller::supportsFeature<>
来通知具体实施。如果控制器支持某项功能,则::type
为std::true_type
,如果不支持,则std::false_type
为Controller
。我为此创建了一个默认结构来禁用任何功能,因此Controller类必须显式启用它提供的任何功能。这似乎是一种将信息从Frontend
传递到Controller
的一种舒适方式,但它有两个主要缺点:
其他人(最终会提供supportsFeature
)很难实现Frontend::something()
,因为必须编写嵌套traits类的特化
我不确定如何评估std::true_type
中某个功能的存在,因为没有任何参数取决于该功能的存在 - 我无法提供合理表达的重载(我不要我想为std::false_type
vs {{1}}重载,因为这根本不能说明问题。我可以简单地使用if语句并依赖编译器来删除死代码。我应该吗?
所以,总结一下:
答案 0 :(得分:1)
我用它来检查确切的签名:
#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 */ }
};
答案 1 :(得分:1)
经过一些尝试/错误,这就是我所说的可接受的解决方案。
当然,使用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 foo\n"; }
static void default_bar( Ctrl & ctrl ) { cout<<"Default bar\n"; }
private:
Ctrl c;
};
最后,有一些Controller
类的例子。一个定义foo
和bar
,另外两个定义每个。{/ p>
struct CtrlFooBar
{
typedef CtrlFooBar WithFoo;
typedef CtrlFooBar WithBar;
void foo() { cout<<"CtrlFB foo\n"; }
void bar() { cout<<"CtrlFB bar\n"; }
};
struct CtrlFoo
{
typedef CtrlFoo WithFoo;
void foo() { cout<<"CtrlFoo foo\n"; }
};
struct CtrlBar
{
typedef CtrlBar WithBar;
void bar() { cout<<"CtrlBar bar\n"; }
};
将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