PiotrNycz
PiotrNycz

Reputation: 24412

boost::variant construction weirdness - its ctor accepts everything

The subject is already touched in this boost-variant-ambiguous-construction question.

But my issue is not with types convertible to each other, but with completely unrelated types.

Simplified example:

// types not related in any way
class A {};
class B {};
class C {};
class D {};

using ABC_variant = boost::variant<A,B,C>;
using D_optional = boost::optional<D>;

The issue was related with the fact that optional on some type was not printable. But, completely unrelated output operator for some variant was trying to accept this boost::optional type (D_optional).

See:

std::ostream& operator << (std::ostream& os, const ABC_variant&)
{
    return os << "ABC";
}

int main() {
    D_optional dopt;
    std::cout << dopt;
}

You can see on ideone - plenty of compilers errors saying that it does not know what do you want to print bool or ABC_variant and in case of ABC_variant it does not know how to convert D_optional to ABC_variant. As I understand that boost::optional is convertible to bool and first alternative is correct I have no idea why it tries to use ABC_variant conversion...

Furthermore I simplified this example even more and give up with boost::optional:

int main() {
    D d;
    std::cout << d;
}

Now, it does not have "bool alternative" and just complaining that it tries to construct ABC_variant from D:

prog.cpp:23:15: required from here /usr/include/boost/variant/variant.hpp:1591:38: error: no matching function for call to 'boost::variant::initializer::initialize(void*, D&)' initializer::initialize(

This here is ostream operator for ABC_variant.

Of course I know that writing ostream operator for D/D_opt will solve the issue - but the problem is with diagnostic: if boost::variant did not accept any type as argument for its constructor, the compiler would tell me simple true - not this bunch of misleading sentences...

I doubt it is in such way by design - maybe there is some fixes ongoing?

Fortunately I and ideone uses the same compiler and boost: gcc4.9 and boost1.58.

Upvotes: 3

Views: 932

Answers (1)

PiotrNycz
PiotrNycz

Reputation: 24412

I have created boost ticket for this problem. Just to clarify what is the real problem:

Real problem is with this boost.variant "converting" constructor accepting any type - there is no restriction, even so natural like that argument type should be convertible to any of this boost.variant instantiation types:

template <typename T>
variant(const T& operand)
{
    convert_construct(operand, 1L);
}

My proposed solution can be something like this:

template <typename T, typename ...C>
struct IsAnyOf;

template <typename T, typename ...C>
struct IsAnyOf<T,T,C...> : std::true_type {};

template <typename T>
struct IsAnyOf<T> : std::false_type {};

template <typename T, typename C1, typename ...C>
struct IsAnyOf<T,C1,C...> : IsAnyOf<T, C...> {};

template <typename T, 
      typename EnableIf = typename std::enable_if<IsAnyOf<VariantType...>::value>::type>
    variant(const T& operand)

But for now - the only solution is not to create any non template function accepting instantiation of boost::variant. So either create function template or aggregate instantiation of boost::variant in some struct type.

So either this:

template <typename T>
typename std::enable_if<std::is_same<T,ABC_variant>::value, std::ostream&>::type
operator << (std::ostream& os, const T&)
{
    return os << "ABC";
}

Or something like this:

struct ABC_variant
{
   boost::variant<A,B,C> v;
   ABC_variant(const A&);
   ABC_variant(const B&);
   ABC_variant(const C&);
};

The issue is fixed in boots.1.62

Upvotes: 2

Related Questions