boost::variant 构造怪异 - 它的 ctor 接受一切

boost::variant construction weirdness - its ctor accepts everything

boost-variant-ambiguous-construction question中已经涉及到该主题。

但我的问题不在于可相互转换的类型,而是完全不相关的类型。

简化示例:

// 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>;

此问题与某些类型的可选内容不可打印有关。但是,某些变体完全不相关的输出运算符试图接受这种 boost::optional 类型 (D_optional).

参见:

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

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

您可以在 ideone 上看到 - 大量编译器错误表明它不知道您要打印什么 boolABC_variant 并且在 [=15= 的情况下] 它不知道如何将 D_optional 转换为 ABC_variant。据我了解 boost::optional 可以转换为 bool 并且第一个选择是正确的我不知道为什么它会尝试使用 ABC_variant 转换...

此外,我进一步简化了这个例子并放弃 boost::optional:

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

现在,它没有“bool 替代方案”,只是抱怨它试图从 D:

构造 ABC_variant

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(

此处ABC_variant 的 ostream 运算符。

当然我知道为 D/D_opt 编写 ostream 运算符将解决问题 - 但问题在于诊断:如果 boost::variant 不接受任何类型作为其构造函数的参数,编译器会告诉我简单的真实 - 而不是这一堆误导性的句子...

我怀疑这是设计使然 - 也许正在进行一些修复?

幸好我和ideone用的是同一个编译器和boost:gcc4.9和boost1.58。

我创建了boost ticket for this problem。只是为了澄清真正的问题是什么:

真正的问题是这个 boost.variant "converting" 构造函数接受任何类型 - 没有限制,即使像参数类型那样自然也应该可以转换为任何这个 boost.variant 实例化类型:

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

我建议的解决方案可以是这样的:

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)

但目前 - 唯一的解决方案是不创建任何接受 boost::variant 实例化的非模板函数。因此,要么创建函数模板,要么在某种结构类型中聚合 boost::variant 的实例化。

所以要么这样:

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";
}

或者像这样:

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

issue固定在boots.1.62