3

Here's my code:

#include <iostream>
#include <variant>
#include <vector>

class A {
public:
    virtual void Foo() = 0;    
};

class B : public A {
public:
    void Foo() override {
        std::cout << "B::Foo()" << std::endl;   
    }
};

class C :public A {
public:
    void Foo() override {
        std::cout << "C::Foo()" << std::endl;   
    }
};

template<typename... Args>
class D {
public:
    template<typename T>
    void Foo() {
        m_variant = T{};
    }

    void Bar() {
      std::get<m_variant.index()>(m_variant).Foo();
    }

private:
    std::variant<std::monostate, Args...> m_variant;
};

int main() {
    D<B, C> d;
    d.Foo<B>();
    d.Bar();

    d.Foo<C>();
    d.Bar();
}

(c.f wandbox.org)

I'm getting the error no matching function for call to 'get' but I don't figure out why. std::variant::index() is constexpr, so it isn't the problem (I tested by putting directly the value 1, but still the same error).
I have a std::monostate to prevent an empty variant (when no args are in typename... Args)

Ardakaniz
  • 155
  • 1
  • 7
  • 5
    `m_variant.index()` is a runtime value, use visitor instead (`std::visit([](auto& e) { e.Foo(); }, m_variant);`). – Jarod42 Aug 28 '17 at 11:28
  • 3
    While it's true that the [`index`](http://en.cppreference.com/w/cpp/utility/variant/index) function is marked `constexpr`, the variable `m_variant` is *not* and can therefore not be used at compile-time. – Some programmer dude Aug 28 '17 at 11:29
  • 3
    The result of `std::variant::index()` is a constant expression only if the variant object it's called on is a constant expression. Yours isn't. – StoryTeller - Unslander Monica Aug 28 '17 at 11:29
  • Oww, yeah, didn't know that, thank you ! So I use `std::visit` instead – Ardakaniz Aug 28 '17 at 11:36
  • BTW, if you use only polymorphism class, you might use (smart) pointer on base class instead of `variant`. – Jarod42 Aug 28 '17 at 11:39
  • In my real code, I have some member variables that couldn't be in the base class, so I have to store the real object not to have slicing problem – Ardakaniz Aug 28 '17 at 11:58

2 Answers2

6

m_variant.index() is a runtime value (as m_variant is not a constant expression).

The way to dispatch is to use visitor, as:

std::visit([](auto& v) { v.Foo(); }, m_variant);

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Could you please shortly explain how `overloaded` works? I've seen [std::visit](http://en.cppreference.com/w/cpp/utility/variant/visit) docs, but it is not explained there. – AMA Aug 28 '17 at 12:12
  • 1
    It allows to group several functors with `operator ()`. That allows to build at call site with lambda instead of writing a dedicated functor with all the overload for `operator()`. – Jarod42 Aug 28 '17 at 12:14
  • 1
    @AMA - See [here](https://stackoverflow.com/questions/45020573/using-stdvisit-with-variadic-template-struct/45020724#45020724) for an explanation. – StoryTeller - Unslander Monica Aug 28 '17 at 12:44
1

Something marked constexpr tells you that in certain situations it can be called at compile time. It does not guarantee it can always be called at compile time.

I the case of variant, index can be called at compile time if the variant itself is a constexpr value. Otherwise it is a runtime method.

You can either read documentation about when something can be called at compile time, or reason about it; in this case, if the type of variant could vary at runtime, how can index be a compile time constant? Remember that only the ttpe and constexpr ness of values, plus if itself is in a compile time context, can be used to reason about "can this be called at compile time".

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524