【问题标题】:Why doesn't this boost::variant::operator= call compile?为什么这个 boost::variant::operator= 不调用编译?
【发布时间】:2017-01-08 17:25:48
【问题描述】:

为什么v = 42 不在这里编译?似乎编译器正在尝试调用Foo 的复制赋值运算符,为什么?我怎样才能让它编译?

#include <boost/variant.hpp>

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

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

wandbox

错误信息是:

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&&) {}
    ^

【问题讨论】:

  • 我怎样才能让它编译?添加默认分配操作Foo&amp; operator=(const Foo&amp;) = default;

标签: c++ boost compiler-errors variant boost-variant


【解决方案1】:

编译器好像在调用Foo的复制赋值操作符,为什么?

这只是令人困惑的措辞。一个移动赋值运算符(你也没有)就足够了。

至于为什么需要移动赋值运算符:boost::variantoperator=是根据另一个boost::variant的赋值来实现的。从您的int 构造一个boost::variant&lt;int, Foo&gt;。然后将v 移动分配给boost::variant&lt;int, Foo&gt;。这需要考虑到它被移动分配 Foo 的可能性,即使这不可能发生。

您可以在编译器的错误消息和 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) );
    }
}

虽然有一个允许跳过临时变体的优化,但只有当变体已经包含 int 时才有可能,无论如何,它无法在编译时确定,因此它不会阻止实例化variant_assign.

【讨论】:

  • "它不直接接受int" — docsoperator= 应该在这里接受inttemplate&lt;typename T&gt; variant &amp; operator=(const T &amp; rhs); 和“T 必须是明确可转换为有界类型之一(即 T1、T2 等)。"
  • @tuple_cat 没错,我写了一个比我预期的更强大的声明。重新措辞,我会仔细检查并尝试用来源备份它。
  • "移动赋值运算符也是复制赋值运算符"...wat?
  • 你确定你没有把它倒过来吗?复制是一种移动(不会改变来源)。
  • @T.C.我一直没能找到我写的时候在想什么。不管它是什么,它充其量是不必要的混乱,可能只是完全错误的。感谢您指出。
【解决方案2】:

来自the docs

每个有界类型都必须满足 MoveAssignable 概念的要求。

并且 MoveAssignable 需要移动分配(尽管据我所知,文档中的任何地方都没有说明这一点)。 Foo 不可移动赋值,因为用户提供的移动构造函数隐式删除了移动赋值运算符。因此,您不符合此运算符的要求。


这似乎是一个 QoI 问题。 operator=(int ) 没有理由需要 Foo::operator=(Foo )。我们可以在编译时确定哪种类型将是新的参与类型(另一个要求),这是我们唯一需要实例化operator= 的类型。如果变体最初是Foo,我们只想破坏原来的Foo并构造一个新的int

【讨论】:

  • 您引用了文档的错误部分。右边是正下方。选择移动赋值运算符,它只要求包含的类型是可移动分配的。
  • @hvd 是的,已修复。奇怪的是 Boost 没有相应的页面。
【解决方案3】:

我认为编译器警告的这一行说明了:

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

如果没有声明任何类型的用户声明的构造函数,编译器只会生成隐式声明的构造函数

这里有完整的描述on Default Constructors

当然可以告诉它生成这个默认分配,但不清楚你的实际代码是否可以。具体来说,不清楚为什么您有自定义移动构造函数。

#include <boost/variant.hpp>

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 2011-09-28
    • 2020-09-20
    相关资源
    最近更新 更多