1

Being inspired by This example, namely by this particular piece of code

// ...
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
// ...
std::visit(overloaded {
    [](auto arg) { std::cout << arg << ' '; },
    [](double arg) { std::cout << std::fixed << arg << ' '; },
    [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);

... I tried to define a class' member of type overloaded, but stumbled on difficulties, which I believe (but not exactly sure) have been caused by inability of the compiler to deduce template's parameters.

template<class ...Ts>
struct overloaded : Ts... {
    using Ts::operator()...;
    constexpr overloaded(Ts... ts) : Ts(ts)... {}
};

struct as{};

class phil_t {
    int i;
    overloaded o { [&](as&) {cout << i << "as"; } }; // <-- HERE
};

That's the output I receive:

../../variant.hpp:21:5: error: invalid use of template-name ‘overloaded’ without an argument list
     overloaded o { [&](as&) {cout << i << "as"; } };
     ^~~~~~~~~~

Tellingly, when I use the same approach to instantiate an object not inside a class, everything works just smoothly.

void varnt() {
    int i = 42;
    auto o = overloaded( [&](as&) {cout << i << "as"; } );
}

Any advice, explaination or hint at some workarounds will be enormously appreciated.

Thank you.

  • @Evg Not exactly. It helped me to understand "why", not "how". But thank you anyway :) – Dmitry Murashov Jan 07 '20 at 12:56
  • It seems that there is no simple way to break cyclic dependencies. One possible solution is to [use type-erasure](https://godbolt.org/z/XUqIMO). Whether it is practical, I'm not sure. – Evg Jan 07 '20 at 13:05

1 Answers1

2

You cannot have a member variable which is not of some specific type which is what you're trying to do in phil_t, as overloaded refers to a template and not a type.

class phil_t;

auto make_overloaded(phil_t& pt) {
    return  overloaded { [&](auto & x) { /* do something with x */ } };
}

class phil_t {
    int i;
    decltype(make_overloaded(std::declval<phil_t&>())) o = make_overloaded(*this);  // <-- HERE
};

Live example here.

The problem with this code is that you want to use phil_t in your lambda, so you can't use my approach because phil_t is incomplete when make_overload is called.

Thanks to @Evg: here's a solution using type erasure that works better than the code above:

#include <iostream>
#include <any>

template<class ...Ts>
struct overloaded : Ts... {
    using Ts::operator()...;
    constexpr overloaded(Ts... ts) : Ts(ts)... {}
};

struct as1 {};
struct as2 {};

class phil_t {
private:
    auto make_overloaded() {
        return overloaded{
            [&](as1& x) { std::cout << "1: " << i << std::endl; },
            [&](as2& x) { std::cout << "2: " << i << std::endl; }
        };
    }

public:
    phil_t() : o(make_overloaded()) {}

    template<class As>
    void foo(As as) {
        std::any_cast<decltype(make_overloaded())>(o)(as);
    }

private:
    int i = 42;
    std::any o;
};

int main() {
    phil_t p;
    p.foo(as1{});
    p.foo(as2{});
}

All credits of this second snippet go to @Evg !

Live example here.

florestan
  • 4,405
  • 2
  • 14
  • 28