c++templatestemplate-argument-deduction

candidate template ignored: couldn't infer template argument 'T' with std::variant


I've been reading all the other questions related to this error and could not find something suitable to my case (or I did not understood them correctly).

I present a minimal example of a class holding a std::variant which represents a variant value (the exception and error handling is omited for brevity):

#include <iostream>
#include <variant>
#include <string>

class Value {
    public:
        Value(const std::string&);
        Value(const int&);

        template<typename T> const T& GetValue() const;

    private:
        std::variant<int, std::string> m_value;
};

Value::Value(const std::string& val):m_value(val) {}
Value::Value(const int& val):m_value(val) {}

template<> const int& Value::GetValue<int>() const {
    return std::get<int>(m_value);
}

template<> const std::string& Value::GetValue<std::string>() const {
    return std::get<std::string>(m_value);
}

int main() {
    Value v_int { 666 };
    Value v_str { "Hola" };

    // int i = v_int.GetValue(); // ERROR
    int i = v_int.GetValue<int>(); // OK but not desireable
    // std::string s = v_str.GetValue(); // ERROR
    std::string s = v_str.GetValue<std::string>(); // OK but not desireable

    std::cout << "i: " << i << " s: " << s << std::endl;

    return 0;
}

What I don't understand is why the // ERROR calls are not deductible if int and std::string are not convertible to each other and target variable destination is the correct type.

Even if manually calling GetValue<T> that would defy the purpose of the wrapping class.

Is calling the non explicit version even possible? Please, if possible do not limit to just present a solution but it would be appreciated an explanation of why it is not working so I can learn from my mistakes.


Solution

  • You might use conversion operator:

    class Value {
    public:
        Value(const std::string&);
        Value(const int&);
    
        auto GetValue() const
        {
            struct {
                const std::variant<int, std::string>& m_value;
    
                operator int() const { return std::get<int>(m_value); }
                operator const std::string&() const { return std::get<std::string>(m_value); }    
            } w{m_value};
            return w;
        }
    
    private:
        std::variant<int, std::string> m_value;
    };
    

    Demo

    But not sure it is a good idea.