如果可用,请使用模板参数的实现,否则使用默认值

时间:2014-09-01 09:29:27

标签: c++ templates

我想为不同的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<>来通知具体实施。如果控制器支持某项功能,则::typestd::true_type,如果不支持,则std::false_typeController。我为此创建了一个默认结构来禁用任何功能,因此Controller类必须显式启用它提供的任何功能。这似乎是一种将信息从Frontend传递到Controller的一种舒适方式,但它有两个主要缺点:

  1. 其他人(最终会提供supportsFeature)很难实现Frontend::something(),因为必须编写嵌套traits类的特化

  2. 我不确定如何评估std::true_type中某个功能的存在,因为没有任何参数取决于该功能的存在 - 我无法提供合理表达的重载(我不要我想为std::false_type vs {{1}}重载,因为这根本不能说明问题。我可以简单地使用if语句并依赖编译器来删除死代码。我应该吗?

  3. 所以,总结一下:

    • 如何将模板类参数中存在方法的信息传递给模板类?
    • 如何根据这些信息在实施之间正确切换?

2 个答案:

答案 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个可调用函数定义两个模板函数。这里只有两个foobar

#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类的例子。一个定义foobar,另外两个定义每个。{/ 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