【问题标题】:Why is `std::optional<T>::operator=` deleted when T contains a `const` data member?当 T 包含 `const` 数据成员时,为什么会删除 `std::optional<T>::operator=`?
【发布时间】:2021-08-08 07:28:56
【问题描述】:

以下代码会导致编译器错误:

#include <optional>

class A {
};
class B {
private:
    const A a;
};

int main()
{
    B b;
    std::optional<B> bo1;
    bo1 = b;
}

例如,在 gcc 上,错误显示为:

main.cpp: In function 'int main()':
main.cpp:12:7: error: uninitialized const member in 'class B'
12 |     B b;
   |       ^
main.cpp:7:13: note: 'const A B::a' should be initialized
7 |     const A a;
  |             ^
main.cpp:14:11: error: use of deleted function 'std::optional<B>& std::optional<B>::operator=(std::optional<B>&&)'
14 |     bo1 = b;
   |           ^
In file included from main.cpp:1:
/lib/gcc-head/include/c++/12.0.0/optional:663:11: note: 'std::optional<B>& std::optional<B>::operator=(std::optional<B>&&)' is implicitly deleted because the default definition would be ill-formed:
663 |     class optional
    |           ^~~~~~~~
/lib/gcc-head/include/c++/12.0.0/optional:663:11: error: use of deleted function 'std::_Enable_copy_move<true, false, true, false, _Tag>& std::_Enable_copy_move<true, false, true, false, _Tag>::operator=(std::_Enable_copy_move<true, false, true, false, _Tag>&&) [with _Tag = std::optional<B>]'
In file included from /lib/gcc-head/include/c++/12.0.0/optional:43,
             from main.cpp:1:
/lib/gcc-head/include/c++/12.0.0/bits/enable_special_members.h:248:5: note: declared here
248 |     operator=(_Enable_copy_move&&) noexcept                         = delete;
    |     ^~~~~~~~

在 MSVC 上,作为另一个示例,错误显示为:

main.cpp
<source>(14): error C2280: 'std::optional<B> &std::optional<B>::operator =(const std::optional<B> &)': attempting to reference a deleted function
C:/data/msvc/14.29.29917-Pre/include\optional(445): note: compiler has generated 'std::optional<B>::operator =' here
C:/data/msvc/14.29.29917-Pre/include\optional(445): note: 'std::optional<B> &std::optional<B>::operator =(const std::optional<B> &)': function was implicitly deleted because a base class invokes a deleted or inaccessible function 'std::_Deleted_move_assign<std::_Optional_construct_base<_Ty>,_Ty> &std::_Deleted_move_assign<std::_Optional_construct_base<_Ty>,_Ty>::operator =(const std::_Deleted_move_assign<std::_Optional_construct_base<_Ty>,_Ty> &)'
        with
        [
            _Ty=B
        ]
C:/data/msvc/14.29.29917-Pre/include\xsmf_control.h(131): note: 'std::_Deleted_move_assign<std::_Optional_construct_base<_Ty>,_Ty> &std::_Deleted_move_assign<std::_Optional_construct_base<_Ty>,_Ty>::operator =(const std::_Deleted_move_assign<std::_Optional_construct_base<_Ty>,_Ty> &)': function was implicitly deleted because a base class invokes a deleted or inaccessible function 'std::_Deleted_copy_assign<std::_Optional_construct_base<_Ty>,_Ty> &std::_Deleted_copy_assign<std::_Optional_construct_base<_Ty>,_Ty>::operator =(const std::_Deleted_copy_assign<std::_Optional_construct_base<_Ty>,_Ty> &)'
        with
        [
            _Ty=B
        ]
C:/data/msvc/14.29.29917-Pre/include\xsmf_control.h(92): note: 'std::_Deleted_copy_assign<std::_Optional_construct_base<_Ty>,_Ty> &std::_Deleted_copy_assign<std::_Optional_construct_base<_Ty>,_Ty>::operator =(const std::_Deleted_copy_assign<std::_Optional_construct_base<_Ty>,_Ty> &)': function was explicitly deleted
        with
        [
            _Ty=B
        ]
Compiler returned: 2

只要我们删除const A a; 中的const 关键字,错误就会消失。 根据referenced standard on std::optional,在某些情况下 = 运算符的某些重载将被删除,具体取决于 std::is_...able_v 测试。在这种情况下,为什么const 关键字会影响其中一些测试?

【问题讨论】:

    标签: c++ c++17 assignment-operator stdoptional


    【解决方案1】:

    构造对象后不能修改 const 数据成员。因此,一个正常工作的operator= 是不可能的。

    【讨论】:

      【解决方案2】:

      optional 使用对象的=

      不能将具有const 数据成员的类分配给。只能构造。

      试试这个:

      B b0;    
      B b1;
      b0=b1;
      

      optional 不起作用,因为 B 不起作用。

      另外,试试static_assert(!std::is_copy_assignable_v&lt;B&gt;);,它通过了。

      现在,std::optional 这里有个“后门”。

      B b;
      std::optional<B> bo1;
      bo1.emplace(b);
      

      构造而不是赋值。

      理论上,Optional 可以依赖于此;但如果内容存在,则需要将其销毁,然后放置一个新对象。这是有风险的,会打乱语义,而且是一个不好的计划默默地做。

      【讨论】:

      • 经验法则,永远不要在右侧使用optionaloperator=T。在我看来,它的语义被破坏了,因为它混合了抽象级别。没有它,毫无疑问有多少事情应该表现出来(比如T constT&amp;)。
      猜你喜欢
      • 2021-06-02
      • 2018-02-10
      • 2019-05-27
      • 1970-01-01
      • 2015-02-27
      • 2021-04-16
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      相关资源
      最近更新 更多