c++structcastingvariant

Implicit conversion from a struct


This is my struct which uses implicit casting on creating variable.

#include <string>
#include <variant>

using namespace std;
using val = variant<int, string, double, bool, long, long long, long double>;

struct value
{
    val innerVal;
    value():innerVal(""){}
    value(const val &c) : innerVal(c) {}

    template <typename T>
    operator T()
    {
          return get<T>(innerVal);
    }

    template <typename V>
    value &operator=(const V &t)
    {
        innerVal = t;
        return *this;
    }
};

This is how I am using it when I construct a variable it works fine but when I assigned an already created variable to value struct it gives error.

int main(int argc, char* argv[])
{
    value h;
    h = "String";
    string m = h;// Here works fine
     string b = "different";
     b = h;//Here it gives error 
}

Compiler error



use of overloaded operator '=' is 
ambiguous (with operand types 
'std::__ndk1::string' (aka 
'basic_string<char, char_traits<char>, 
allocator<char> >') and 'value')

        b = h;

I want to implicitly construct the variable without encountering this error can someone explain how to resolve this issue.


Solution

  • The assignment b = h is ambiguous, because std::string has different operator=. Because you have a templated conversion operator your compile does not know which overloaded operator= of std::string to take.

    You can constrain the types of conversions allowed from value to allow only implicit conversions to one of the member variants type. The following struct will provide a boolean telling us, whether type T is contained in Type's template parameters, using a fold expression:

    template<typename, typename>
    struct has_template_param;
    
    template<typename T, template<typename ...> typename Type, typename ...Ts>
    struct has_template_param<T, Type<Ts...>> {
        static constexpr bool value = (... || std::is_same_v<T, Ts>);
    };
    

    Then the templated conversion operator can be constrained with SFINAE:

    template<typename T, typename = std::enable_if_t<has_template_param<T, val>::value>>
    operator T() {
        return std::get<T>(innerVal);
    }
    

    Using C++20 concepts, it can be done more conveniently via

    template<typename T, typename Type>
    concept has_param = has_template_param<T, Type>::value;
    

    and

    template<has_param<val> T>
    operator T() {
        return std::get<T>(innerVal);
    }
    

    See demo