call the derived template function

Your example is a bit contrived…. the visitor pattern is made for and works best with std::variant<>.. Have you tried visitor pattern #4 from the example in the std::visit docmentation at cppreference? (https://en.cppreference.com/w/cpp/utility/variant/visit)

// needed declaration cut & pasted from the example at cppreference 
// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

// using variant instead of class hierarchy.
struct ClassA {  int member_of_a; };
struct ClassB {  int member_of_b; };

using Variant = std::variant<ClassA, ClassB>;

Variant v = ClassA{42};

int n = 0;
n += std::visit(overloaded{
    [](const ClassA& a) {  return a.member_of_a; },
    [](const ClassB& b) {  return b.member_of_b; }
  }, v);

// or, with additional parameters, you could call member functions or free functions as well. 
std::visit(overloaded{
    [](const ClassA& a, int x) {  a.member_of_a = x; },
    [](const ClassB& b, int x) {  b.member_of_b = x; }
  }, v, 10);

// This also works with class hierarchy.
struct ClassC : ClassB { int member_of_c; };

using Variant2 = std::variant<ClassA, ClassB, ClassC>;
Variant2 v2{ClassC{}};

n += std::visit(overloaded{
    [](const ClassA& a) {  return a.member_of_a; },
    [](const ClassB& b) {  return b.member_of_b; },
    [](const ClassC& c) {  return c.member_of_c; }
  }, v);

// or, using same processing for ClassB and ClassC objects, which are related. 
n += std::visit(overloaded{
    [](const ClassA& a) {  return a.member_of_a; },
    [](const ClassB& b) {  return b.member_of_b; }
  }, v);

You’ll note that ClassA and ClassB are totally independant from each other… That’s when the visitation paradigm really shines.

This means it works well with templates.

temptate <typename T>
struct Template { T value; }

using Variant = std::variant<Template<int>, Template<float>, Template<double>>;

Variant v = Template<int>{};

// added some multiplexing, just to spice up the example a bit.
std::visit(overloaded{
    [](const Template<int>& a, int x, float) {  a.value += x; },
    [](const Template<float>& b, int, float y) {  b.value += y; },
    [](const Template<double>& b, int x, float y) {  b.value += std::sin(x * double(y)); }
  }, v, 10, 42.f);

If you do not want to work with variants, then you’ll have to use class hierachy and call virtual functions to get the effect you want. No need for visitation at all in this case but you’ll be limited to the hierarchy for the objects that your code will work with.

struct Base { virtual int inc() = 0; }
struct ClassA {  int int() override { return 42; } };
struct ClassB {  int int() override { return 10; } };

std::unique_ptr<Base> p = std::make_unique<ClassA>();

int n = 0;
n += p->inc();

For storage in std containers, you can use std::variant<> as you would any regular type, with the usual restrictions on default construction, copy, move, etc… resting on your template class.

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top