简体   繁体   English

将虚拟功能融入家庭

[英]Putting virtual functions into a family

Given 给定

class A {
    public:
        virtual int foo (int) const = 0;
        virtual void bar (char, double) const = 0;
};

class B : public A {
    virtual int foo (int) const {std::cout << "B::foo() called.\n";  return 3;}
    virtual void bar () const {std::cout << "B::bar() called.\n";}
};

class C : public B {
    virtual int foo (int) const {std::cout << "C::foo() called.\n";  return 8;}
    virtual void bar (char, double) const {std::cout << "C::bar() called.\n";}
};

I want to put foo and bar (and other virtual functions of A ) into a template family of functions. 我想将foobar (以及A其他虚拟函数)放入函数的模板系列中。 Here's what I came up with so far: 到目前为止,这是我想到的:

#include <iostream>

enum Enum {Foo, Bar};

template <Enum> struct EnumTraits;

template <> struct EnumTraits<Foo> { using return_type = int; };
template <> struct EnumTraits<Bar> { using return_type = void; };

class A {
    template <Enum> class Execute;
public:
    virtual int foo (int) const = 0;
    virtual void bar (char, double) const = 0;
    template <Enum E, typename... Args>
    typename EnumTraits<E>::return_type execute(Args&&... args) const {
        return Execute<E>(this)(std::forward<Args>(args)...);
    }
};

template <>
class A::Execute<Foo> {
    const A* a;
public:
    Execute (const A* a_) : a(a_) {}
    template <typename... Args>
    int operator()(Args&&... args) const {return a->foo(std::forward<Args>(args)...);}
};

template <>
class A::Execute<Bar> {
    const A* a;
public:
    Execute (const A* a_) : a(a_) {}
    template <typename... Args>
    void operator()(Args&&... args) const {a->bar(std::forward<Args>(args)...);}
};

class B : public A {
    virtual int foo (int) const {std::cout << "B::foo() called.\n";  return 3;}
    virtual void bar () const {std::cout << "B::bar() called.\n";}
};

class C : public B {
    virtual int foo (int) const {std::cout << "C::foo() called.\n";  return 8;}
    virtual void bar (char, double) const {std::cout << "C::bar() called.\n";}
};

int main() {
    A* c = new C;

    int n = c->foo(5);  // C::foo() called.
    c->bar(3, 'c');  // C::bar() called.

    n = c->execute<Foo>(5);  // C::foo() called.
    c->execute<Bar>(3, 'c');  // C::bar() called.
}

But the specializations A::Execute<Foo> and A::Execute<Bar> look near-identical and should ideally be left unspecialized (especially if there are many other virtual functions than foo and bar ). 但是专业化A::Execute<Foo>A::Execute<Bar>看起来几乎相同,并且理想情况下应该不进行专业化(特别是如果有许多其他虚拟函数,而不是foobar )。 Written something like: 写类似:

template <Enum N>
class A::Execute {
    const A* a;
public:
    Execute (const A* a_) : a(a_) {}
    template <typename... Args>
    int operator()(Args&&... args) const {return a->???(std::forward<Args>(args)...);}

};

How to fill in that ??? 如何填写??? part? 部分? Ideally, I was hoping to use the EnumTraits class already present. 理想情况下,我希望使用已经存在的EnumTraits类。

Here's my attempt. 这是我的尝试。 I have replaced the enum by struct s that are used as tags and EnumTraits by TagTraits . 我已经用用作标签的struct替换了enum ,并用EnumTraitsTagTraits I prefer the struct approach since it allows for new tags to be added without affecting the existing tags. 我更喜欢struct方法,因为它允许添加新标签而不影响现有标签。

#include <iostream>
#include <functional>

template <typename T> struct TagTraits;

// Generic implementation of A based on TagTraits.
class A {

   template <typename Tag, typename... Args>
      class Execute {
         const A* a;
         public:
         Execute (const A* a_) : a(a_) {}
         typename TagTraits<Tag>::return_type operator()(Args&&... args) const
         {
            return (a->*(TagTraits<Tag>::get_funtion_ptr()))(std::forward<Args>(args)...);
         }
      };

   public:

   virtual int foo (int) const = 0;
   virtual void bar (char, double) const = 0;

   template <typename Tag, typename... Args>
      typename TagTraits<Tag>::return_type execute(Args&&... args) const
      {
         return Execute<Tag, Args...>(this)(std::forward<Args>(args)...);
      }
};

// tag for foo and the corresponding TagTraits
struct foo_tag {};

template <> struct TagTraits<foo_tag>
{ 
   using return_type = int;
   static decltype(&A::foo) get_funtion_ptr(){ return &A::foo;}
};

// tag for bar and the corresponding TagTraits
struct bar_tag {};

template <> struct TagTraits<bar_tag>
{
   using return_type = void;
   static decltype(&A::bar) get_funtion_ptr(){ return &A::bar;}
};

// Derived classes of A.

class B : public A {
   virtual int foo (int) const {std::cout << "B::foo() called.\n";  return 3;}
   virtual void bar (char, double) const {std::cout << "B::bar() called.\n";}
};

class C : public B {
   virtual int foo (int) const {std::cout << "C::foo() called.\n";  return 8;}
   virtual void bar (char, double) const {std::cout << "C::bar() called.\n";}
};

// Test B
void test_B()
{
   A* aPtr = new B;

   int n = aPtr->foo(5);  // B::foo() called.
   aPtr->bar(3, 'c');  // B::bar() called.

   n = aPtr->execute<foo_tag>(5);  // B::foo() called.
   aPtr->execute<bar_tag>(3, 'c');  // B::bar() called.
}

// Test C
void test_C()
{
   A* aPtr = new C;

   int n = aPtr->foo(5);  // C::foo() called.
   aPtr->bar(3, 'c');  // C::bar() called.

   n = aPtr->execute<foo_tag>(5);  // C::foo() called.
   aPtr->execute<bar_tag>(3, 'c');  // C::bar() called.
}

int main()
{
   test_B();
   test_C();
}

Output: 输出:

B::foo() called.
B::bar() called.
B::foo() called.
B::bar() called.
C::foo() called.
C::bar() called.
C::foo() called.
C::bar() called.

You cannot combine inheritance and polymorphism with template metaprogramming. 您不能将继承和多态与模板元编程结合在一起。 What you are trying to do is use compile time mechanisms to call the right functions. 您正在尝试使用编译时间机制来调用正确的函数。 While that is possible you cannot dereference a pointer to call the right function and expect that to run in compile time. 尽管这是可能的,但您不能取消引用指针来调用正确的函数并期望它在编译时运行。

Essentially what you have done is wrap in the inheritance hierarchy a hierarchy of partial template specializations. 从本质上讲,您所做的就是在继承层次结构中包装部分模板专业化层次结构。 That's a little confusing. 这有点令人困惑。 If you dereference a pointer that has an inheritance hierarchy the call will get resolved by lookup in the virtual table. 如果取消引用具有继承层次结构的指针,则该调用将通过在虚拟表中进行查找来解决。

You can pull of template compile time dispatch too, but that would be a little different. 您也可以拉模板编译时间调度,但这会有所不同。 You would need to use SFINAE to create an interface and work from there 您将需要使用SFINAE创建一个接口并从那里开始工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM