简体   繁体   English

为什么boost :: variant :: operator =不会编译?

[英]Why doesn't this boost::variant::operator= call compile?

Why doesn't v = 42 compile here? v = 42为什么不在这里编译? It seems the compiler is trying to call the copy assignment operator of Foo , why? 看来编译器正在尝试调用Foo的副本分配运算符,为什么呢? How can I get it to compile? 如何获得它进行编译?

#include <boost/variant.hpp>

struct Foo {
    Foo(Foo&&) { }
};

int main() {
    boost::variant<int, Foo> v;
    v = 42;
}

wandbox wandbox

The error message is: 错误消息是:

In file included from prog.cc:1:
In file included from /usr/local/boost-1.62.0/include/boost/variant.hpp:17:
/usr/local/boost-1.62.0/include/boost/variant/variant.hpp:619:21: error: object of type 'Foo' cannot be assigned because its copy assignment operator is implicitly deleted
        lhs_content = ::boost::detail::variant::move(*static_cast<T* >(rhs_storage_));
                    ^
/usr/local/boost-1.62.0/include/boost/variant/detail/visitation_impl.hpp:112:20: note: in instantiation of function template specialization 'boost::detail::variant::move_storage::internal_visit<Foo>' requested here
    return visitor.internal_visit(
                   ^
/usr/local/boost-1.62.0/include/boost/variant/detail/visitation_impl.hpp:154:13: note: in instantiation of function template specialization 'boost::detail::variant::visitation_impl_invoke_impl<boost::detail::variant::move_storage, void *, Foo>' requested here
    return (visitation_impl_invoke_impl)(
            ^
/usr/local/boost-1.62.0/include/boost/variant/detail/visitation_impl.hpp:240:11: note: in instantiation of function template specialization 'boost::detail::variant::visitation_impl_invoke<boost::detail::variant::move_storage, void *, Foo, boost::variant<int, Foo>::has_fallback_type_>' requested here
        , BOOST_VARIANT_AUX_APPLY_VISITOR_STEP_CASE
          ^
/usr/local/boost-1.62.0/include/boost/preprocessor/repetition/repeat.hpp:29:26: note: expanded from macro 'BOOST_PP_REPEAT'
# define BOOST_PP_REPEAT BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))
                         ^
/usr/local/boost-1.62.0/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
#    define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
                               ^
/usr/local/boost-1.62.0/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
#    define BOOST_PP_CAT_I(a, b) a ## b
                                 ^
<scratch space>:128:1: note: expanded from here
BOOST_PP_REPEAT_1
^
/usr/local/boost-1.62.0/include/boost/variant/variant.hpp:2384:33: note: in instantiation of function template specialization 'boost::detail::variant::visitation_impl<mpl_::int_<0>, boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<2>, int, boost::mpl::l_item<mpl_::long_<1>, Foo, boost::mpl::l_end> > >, boost::mpl::l_iter<boost::mpl::l_end> >, boost::detail::variant::move_storage, void *, boost::variant<int, Foo>::has_fallback_type_>' requested here
        return detail::variant::visitation_impl(
                                ^
/usr/local/boost-1.62.0/include/boost/variant/variant.hpp:2398:16: note: in instantiation of function template specialization 'boost::variant<int, Foo>::internal_apply_visitor_impl<boost::detail::variant::move_storage, void *>' requested here
        return internal_apply_visitor_impl(
               ^
/usr/local/boost-1.62.0/include/boost/variant/variant.hpp:2125:19: note: in instantiation of function template specialization 'boost::variant<int, Foo>::internal_apply_visitor<boost::detail::variant::move_storage>' requested here
            this->internal_apply_visitor(visitor);
                  ^
/usr/local/boost-1.62.0/include/boost/variant/variant.hpp:2171:13: note: in instantiation of member function 'boost::variant<int, Foo>::variant_assign' requested here
            variant_assign( detail::variant::move(temp) );
            ^
/usr/local/boost-1.62.0/include/boost/variant/variant.hpp:2189:9: note: in instantiation of function template specialization 'boost::variant<int, Foo>::move_assign<int>' requested here
        move_assign( detail::variant::move(rhs) );
        ^
prog.cc:9:7: note: in instantiation of function template specialization 'boost::variant<int, Foo>::operator=<int>' requested here
    v = 42;
      ^
prog.cc:4:5: note: copy assignment operator is implicitly deleted because 'Foo' has a user-declared move constructor
    Foo(Foo&&) {}
    ^

It seems the compiler is trying to call the copy assignment operator of Foo, why? 看来编译器正在尝试调用Foo的副本分配运算符,为什么?

That's just confusing wording. 那只是措辞混乱。 A move assignment operator (which you don't have either) would suffice. 一个移动分配运算符(您也没有)就足够了。

As for why a move assignment operator is needed: boost::variant 's operator= is implemented in terms of assignment from another boost::variant . 至于为什么需要移动赋值运算符: boost::variantoperator=是根据另一个boost::variant的赋值实现的。 From your int , a boost::variant<int, Foo> is constructed. 从您的int ,构建boost::variant<int, Foo> v is then move-assigned that boost::variant<int, Foo> . 然后将v分配给boost::variant<int, Foo> Which needs to account for the possibility that it's being move-assigned a Foo , even though that cannot happen. 这需要考虑它被移动分配给Foo的可能性,即使那不可能发生。

You can see the variant_assign helper method responsible for this in your compiler's error message, and in variant.hpp : 您可以在编译器的错误消息以及variant.hpp看到负责此操作的variant_assign帮助程序方法:

template <typename T>
void move_assign(T&& rhs)
{
    // If direct T-to-T move assignment is not possible...
    detail::variant::direct_mover<T> direct_move(rhs);
    if (this->apply_visitor(direct_move) == false)
    {
        // ...then convert rhs to variant and assign:
        //
        // While potentially inefficient, the following construction of a
        // variant allows T as any type convertible to one of the bounded
        // types without excessive code redundancy.
        //
        variant temp( detail::variant::move(rhs) );
        variant_assign( detail::variant::move(temp) );
    }
}

While there's an optimisation there that allows the temporary variant to be skipped, that's only possible when the variant already contains an int , and regardless, it cannot be determined at compile time, so it wouldn't have prevented the instantiation of variant_assign . 尽管有一个优化允许跳过临时变量,但只有在变量已经包含int的情况下才有可能,并且无论如何,它都无法在编译时确定,因此不会阻止variant_assign的实例化。

From the docs : 文档

Every bounded type must fulfill the requirements of the MoveAssignable concept. 每个有界类型都必须满足MoveAssignable概念的要求。

And MoveAssignable requires move assignment (although this isn't spelled out anywhere in the documentation as far as I can find). 而且MoveAssignable需要移动分配(尽管据我所知,这在文档的任何地方都没有说明)。 Foo isn't move-assignable because the user-provided move constructor implicitly deletes the move-assignment operator. Foo不可移动分配,因为用户提供的move构造函数会隐式删除move-assignment运算符。 Hence, you don't meet the requirements for this operator. 因此,您不符合该操作员的要求。


This seems to be a QoI issue. 这似乎是一个QoI问题。 There's no reason that operator=(int ) should require Foo::operator=(Foo ) . 没有理由operator=(int )应该要求Foo::operator=(Foo ) We can determine at compile-time which type will be the new engaged type (another one of the requirements), and that is the only one for which we would need to instantiate operator= . 我们可以在编译时确定哪种类型将是新的参与类型(需求的另一种),而这是我们唯一需要实例化operator= If the variant was initially Foo , we would simply want to destroy the original Foo and construct a new int . 如果变体最初是Foo ,则我们只想破坏原始的Foo并构造一个新的int

I think this line of the compiler warning is telling: 我认为编译器警告的这一行说明:

prog.cc:4:5: note: copy assignment operator is implicitly deleted because 'Foo' has a user-declared move constructor

The compiler will only generate implicitly declared constructors if no user-declared constructors of any kind are declared 如果未声明任何类型的用户声明的构造函数,则编译器将仅生成隐式声明的构造函数

Full description here on Default Constructors 关于默认构造函数的完整说明

Can of course just tell it to generate this default assignment, but unclear of your actual code and if that'd be ok. 当然可以告诉它生成此默认分配,但是不清楚您的实际代码,如果可以的话。 Specifically, it's unclear why you have a custom move constructor. 具体来说,目前尚不清楚为什么要使用自定义移动构造函数。

#include <boost/variant.hpp>

struct Foo {
    Foo(Foo&&) { }
    Foo& operator=(const Foo&) = default;
};

int main() {
    boost::variant<int, Foo> v;
    v = 42;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM