增加访问量中可能存在的错误

Possible bug in boost visitation

我收到以下错误消息:

/usr/include/boost/variant/detail/visitation_impl.hpp:207: typename Visitor::result_type boost::detail::variant::visitation_impl(int, int, Visitor &, VPCV, mpl::true_, NBF, W *, S *) [W = mpl_::int_<20>, S = boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_end>, boost::mpl::l_iter<boost::mpl::l_end> >, Visitor = boost::detail::variant::copy_into, VPCV = const void *, NBF = boost::variant<TypeInfo, int, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_>::has_fallback_type_]: Assertion `false' failed.

当我将 std::vector<A> 作为参数按值传递给某个函数并且 A 被定义为 using A = boost::variant<B, int>; 时,就会发生这种情况。 简单来说,B定义如下:

class B
{
    Data data;
    std::vector< boost::variant<std::shared_ptr<C>, B> > vec;
};

B 是错误消息中的 TypeInfo

void func(std::vector<B> vec); //signature

auto result = func(that_vector_with_variants); //that line causes an error

我在这里发现了类似的错误https://svn.boost.org/trac/boost/ticket/5146

我的问题是:这是boost中的错误吗?我怎样才能使我的代码工作?

更新:

我认为我必须补充一点,如果我将 std::vector<boost::variant<std::shared_ptr<C>, B> > vec; 更改为 std::vector<boost::variant<C*, B> > vec; 那么一切正常。

走出困境,通过阅读该错误报告,您可能无意中做了这样的事情:

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

using P = boost::shared_ptr<int>;
using V = boost::variant<P, int>;
using C = std::vector<V>;

int main() {
    P p = boost::make_shared<int>(42);
    assert(p.unique());

    C v = { p };
    assert(!p.unique());

    v = std::move(v);
    assert(!p.unique()); // WHOOPS assert fails
}

当然这个样本是人为的²,因为我不知道你的实际 code/use 案例。

最后一个断言失败。这是因为在内部,操作的顺序是这样的:原始变体被清除(并且包含的​​值被破坏)分配新值之前:

"Never-Empty" Guarantee

While the never-empty guarantee might at first seem "obvious," it is in fact not even straightforward how to implement it in general

Cf. The "Ideal" Solution: False Hopes from Boost Variant "Design Overview"

因此,如果 p 中的 int 没有 "reference",此操作将在实例重新分配之前将其销毁。

在这种情况下,它看起来更像是一种需要避免的使用模式(本质上是使用智能指针进行就地修改 ¹)。在这种特殊情况下,这些似乎有效(在我的 compiler/library 实施中):

std::move(v.begin(), v.end(), v.begin());
// or
std::copy(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()), v.begin());
// or
v.assign(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));

但我当然更喜欢这样写

C tmp = std::move(v); 
v = std::move(tmp);

只是因为没有其他保证。


¹ 总是有危险!甚至 Scott Meyers 在他的 "More Effective C++" 中也感受到了这一点,参见 erratum p.200/202

² 相关:What does the standard library guarantee about self move assignment?