Opened 7 years ago

Closed 6 years ago

#11602 closed Bugs (fixed)

boost.variant constructor accepts any type as parameter

Reported by: piotrwn1@… Owned by: Antony Polukhin
Milestone: Boost 1.62.0 Component: variant
Version: Boost 1.58.0 Severity: Problem
Keywords: Cc: raad@…

Description

There is one "converting constructor" in boost.variant which accepts any type - even those which are not convertible to instantiation types of boost.variant. This causes that writing function accepting some specific variant instantiation cause that this function is on candidate list of any type in your program.

The problem is with this constructor:

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

And the small code presenting the issue:

#include <iostream>
#include <boost/variant.hpp>

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

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

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

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

As described in http://stackoverflow.com/questions/32275725/boostvariant-construction-weirdness-its-ctor-accepts-everything

compiler (gcc4.9) tries to use ostream operator with this diagnostic:

/usr/include/boost/variant/variant.hpp:1591:38: error: no matching function for call to 'boost::variant<A, B, C>::initializer::initialize(void*, D&)'

initializer::initialize(

However it should simple report that ostream operator for D is missing.

One simple way is to declare this boost.variant constructor as explicit (what probably would impact to many client code) or to make some restriction in this constructor like this C++11 way:

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)


BR, Piotr Nycz (piotrwn1 @ gmail com)

Change History (6)

comment:1 by piotrwn1@…, 7 years ago

Maybe better would to use IsConvertibleToAnyOf instead of IsAnyOf:

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

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


template <typename T, typename C1, typename ...C>
struct IsConvertibleToAnyOf<T,C1,C...> : std::integral_constant<bool,
    std::is_convertible<T,C1>::value or
    IsConvertibleToAnyOf<T,C...>::value> {};

BR,

Piotr Nycz

comment:2 by raad@…, 7 years ago

Cc: raad@… added

comment:3 by raad@…, 7 years ago

This problem also makes boost::variant unusable in C++17's std::tuple as implemented in MSVC 14 Update 2, affecting boost::signals2 (ticket #12123).

Ticket #5871 describes the same problem.

comment:4 by Antony Polukhin, 6 years ago

Milestone: To Be DeterminedBoost 1.62.0
Owner: changed from ebf to Antony Polukhin
Status: newassigned

comment:5 by Antony Polukhin, 6 years ago

Fixed in b3650685 develop, will be merged to the master branch as soon as the regression tests will cycle.

comment:6 by Antony Polukhin, 6 years ago

Resolution: fixed
Status: assignedclosed
Note: See TracTickets for help on using tickets.