Reputation: 24412
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
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