Search code examples
c++implicit-conversionboost-variant

boost::variant implicit cast to string


I have a boost::variant with different types, where one is a (const) void pointer and another a string.

boost::variant<std::string, void const*>;

The problem is, if i want to use it with a c-string it casts it to the void pointer instead the string.

boost::variant<std::string, void const*> foo;
foo = "bar";
std::cout << foo.which(); // prints 1 -> void const*

If I remove the constness of the pointer it will cast it to the string.

boost::variant<std::string, void*> foo; // no const pointer
foo = "bar";
std::cout << foo.which(); // prints 0 -> string

Is there an easy way to make boost::variant implicitly cast the c-string to a std::string?

UPDATE

I know I can cast explicit with:

foo = std::string("bar");

But I would like to avoid the explicit casting.


Solution

  • You can provide your own variant template by inheriting from boost::variant. The correct conversion from const char* to std::string is achieved through overloading the operator=:

    #include <iostream>
    #include <string>
    #include <boost/variant.hpp>
    
    template <typename... Types>
    struct my_variant : public boost::variant<Types...>
    {
        using boost::variant<Types...>::variant;
    
        auto operator=(const char* rhs)
        {
            return boost::variant<Types...>::operator=(std::string(rhs));
        }
    };
    
    int main() 
    {
        my_variant<std::string, void const*> foo;
        foo = "bar";
        std::cout << foo.which(); 
        return 0;
    }
    

    Outputs "0" as desired.

    Live example: https://ideone.com/ZppUla

    This idea could be even more generalized through the use of a traits class which specifies the type mapping:

    template <template <typename> class conv_traits, typename... Types>
    struct my_variant : public boost::variant<Types...>
    {
        using boost::variant<Types...>::variant;
    
        template <typename T>
        auto operator=(T rhs)
        {
            return boost::variant<Types...>::operator=(static_cast<typename conv_traits<T>::type>(rhs));
        }
    };
    
    template <typename T>
    struct conversion_traits
    {
        typedef T type;
    };
    
    template <>
    struct conversion_traits<const char*>
    {
        typedef std::string type;
    };
    
    my_variant<conversion_traits, std::string, void const*> foo;
    

    Live example: https://ideone.com/AXUqTv