【问题标题】:Putting virtual functions into a family将虚拟功能融入家庭
【发布时间】:2015-11-21 07:29:33
【问题描述】:

给定

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";}
};

我想将foobar(以及A 的其他虚函数)放入函数的模板族中。到目前为止,这是我想出的:

#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.
}

但特化 A::Execute&lt;Foo&gt;A::Execute&lt;Bar&gt; 看起来几乎相同,理想情况下应该保持非特化(特别是如果有许多其他虚函数而不是 foobar)。写成这样:

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)...);}

};

如何填写???部分?理想情况下,我希望使用已经存在的 EnumTraits 类。

【问题讨论】:

  • 这是一项智力练习吗?您是否正在尝试寻找真正的设计问题的答案?
  • 我想以通用方式在其他地方使用模板系列。真的很有帮助。

标签: c++ function templates c++11 virtual


【解决方案1】:

这是我的尝试。我已将enum 替换为用作标签的structs,并将EnumTraits 替换为TagTraits。我更喜欢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();
}

输出:

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

【讨论】:

  • 出于好奇,&amp;A::foo 的显式类型是什么?那就是我遇到麻烦的地方。 int(A::*)(int) const 未被接受。
  • @prestokeys 那是类型,但它不应该这样写为返回类型。应该是int (A::*get_funtion_ptr())(int) const {...}
  • @ 0x499602D2 那也不编译。但没关系,decltype 可以节省时间。
  • @prestokeys,不知道为什么这对你不起作用。在ideone.com/XspEyN 上查看它。
【解决方案2】:

您不能将继承和多态性与模板元编程结合起来。您要做的是使用编译时机制来调用正确的函数。虽然这是可能的,但您不能取消引用一个指针来调用正确的函数并期望它在编译时运行。

您所做的基本上是在继承层次结构中包装部分模板特化的层次结构。这有点令人困惑。如果您取消引用具有继承层次结构的指针,则该调用将通过在虚拟表中查找来解决。

您也可以提取模板编译时调度,但这会有点不同。您需要使用 SFINAE 创建一个界面并从那里开始工作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-04
    • 2014-05-12
    • 1970-01-01
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 1970-01-01
    相关资源
    最近更新 更多