C ++模板当模板的功能不一样时

时间:2015-08-04 08:09:06

标签: c++ templates instantiation

现在我有一个模板类

template <class T>
class b
{
    void someFunc() {
        T t;
        t.setB();
    }
};

我知道模板T只会被实例化为2个类。

class D
{
public:
     void setB();
};

class R
{
public:
     void SetB();
};

我们可以看到,D类的功能名称 setB 与R的功能 SetB 不同。所以在模板类b中我不仅可以使用setB。如果我不能修改D或R,是否有一些方法?我可以在模板类中添加一些包装器或技巧来解决这个问题吗?

3 个答案:

答案 0 :(得分:3)

也许特质课可以帮助你:

struct Lower {};
struct Upper {};

// trait for most cases
template <typename T>
struct the_trait {
    typedef Lower Type;
};
// trait for special cases
template <>
struct the_trait<R> {
    typedef Upper Type;
};

template <class T>
class b {
public:
    void foo() {
        foo_dispatch(typename the_trait<T>::Type());
    }
private:
    void foo_dispatch(Lower) {
        T t;
        t.setB();
    }
    void foo_dispatch(Upper) {
        T t;
        t.SetB();
    }
};

正如@Arunmu所指出的,这种技术也被称为Tag Dispatching

答案 1 :(得分:2)

您可以为具有不同语义的类专门化您的模板:

template<>
class b<R>
{
    void doWork() {
        R obj;
        obj.SetB();
        // or R::SetB() if it was a static method.
    }
};

答案 2 :(得分:0)

您也可以使用SFINAE检查是否存在自我编程的特征,而不是使用自编程特征。

如果要切换被调用方法,则每个类中只能存在其中一个方法。如果检查发现多于一种经过测试的方法,我提供的方法将无效!

以下示例是为C ++ 14编写的,但如果用自己实现的库函数替换新的库函数,也可以与c ++ 03一起使用(这当然不方便)

测试类has_Foo和has_Bar也可以嵌入到预处理器宏中,但我将其编写为扩展以使事情更容易阅读。

注释中解释了它的工作原理以及为什么需要更多中间步骤。见下文!

    #include <iostream>


    // First we write two classes as example. Both classes represents
    // external code which you could NOT modify, so you need an
    // adapter to use it from your code.
    class A
    {
        public:
            void Foo() { std::cout << "A::Foo" << std::endl; }
    };

    class B
    {
        public:
            void Bar() { std::cout << "B::Bar" << std::endl; }
    };

    // To benefit from SFINAE we need two helper classes which provide
    // a simple test functionality. The solution is quite easy...
    // we try to get the return value of the function we search for and
    // create a pointer from it and set it to default value nullptr.
    // if this works the overloaded method `test` returns the data type
    // one. If the first test function will not fit, we cat with ... all
    // other parameters which results in getting data type two.
    // After that we can setup an enum which evaluates `value` to
    // boolean true or false regarding to the comparison function.


    template <typename T>
    class has_Foo
    {
        using one = char;
        using two = struct { char a; char b;};
        template <typename C> static one test( typename std::remove_reference<decltype(std::declval<C>().Foo())>::type*  );  
        template <typename C> static two test( ... ) ; 

        public:
        enum { value = sizeof(test<T>(0)) == sizeof(char) };
        enum { Yes = sizeof(test<T>(0)) == sizeof(one) };
        enum { No = !Yes };
    };

    template <typename T>
    class has_Bar
    {
        using one = char;
        using two = struct { char a; char b;};
        template <typename C> static one test( typename std::remove_reference<decltype(std::declval<C>().Bar())>::type*  );  
        template <typename C> static two test( ... ) ; 

        public:
        enum { value = sizeof(test<T>(0)) == sizeof(char) };
        enum { Yes = sizeof(test<T>(0)) == sizeof(one) };
        enum { No = !Yes };
    };


    // Now in your adapter class you can use the test functions
    // to find out which function exists. If your class
    // contains a Foo function the first one compiles and if the
    // the class contains a Bar function the second one fits. SFINAE
    // disable the rest.
    // We need a call helper here because SFINAE only 
    // fails "soft" if the template parameter can deduced from the
    // given parameters to the call itself. So the method
    // Call forwards the type to test "T" to the helper method as 
    // as explicit parameter. Thats it!
    template <typename T>
    class X: public T
    {
        public:
            template < typename N, std::enable_if_t< has_Foo<N>::value>* = nullptr>
                void Call_Helper() { this->Foo(); }

            template < typename N, std::enable_if_t< has_Bar<N>::value>* = nullptr>
                void Call_Helper() { this->Bar(); }

            void Call() { Call_Helper<T>(); }
    };


    int main()
    {
        X<A> xa;
        X<B> xb;

        xa.Call();
        xb.Call();
    }