#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 int (A::*get_funtion_ptr())(int) const { 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();
}
