简体   繁体   中英

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

Here's my attempt. I have replaced the enum by struct s that are used as tags and EnumTraits by TagTraits . I prefer the struct approach since it allows for new tags to be added without affecting the existing tags.

#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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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