4

The following code would lead to compiler errors:

#include <optional>

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

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

On gcc, for example, the error reads:

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;
    |     ^~~~~~~~

On MSVC, as another example, the error reads:

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

As long as we remove the const keyword in const A a;, the errors disappear. According to the referenced standard on std::optional, there are cases when certain overloads of the =operator would be removed, depending on the std::is_...able_v tests. In this case, why would the const keyword affect some of these tests?

DXZ
  • 451
  • 4
  • 14

2 Answers2

9

optional uses the object's =.

A class with a const data member cannot be assigned to. It can only be constructed.

Try this:

B b0;    
B b1;
b0=b1;

optional doesn't work because B doesn't.

Also, try static_assert(!std::is_copy_assignable_v<B>);, which passes.

now, std::optional has a "back door" here.

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

which constructs instead of assigns.

Optional could, in theory, fall back on this; but if the contents exist, it would need to destroy it then emplace a new object. That is risky, messes up semantics, and a bad plan to do silently.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Rule of thumb, never use `optional`'s `operator=` with `T` on the right hand side. It's semantics is broken in my opinion because it mixes levels of abstraction. Without it, there is no doubt how many things should behave (like `T const` or `T&`). – alfC May 19 '21 at 04:19
2

A const data member can't be modified after the object is constructed. Thus a properly working operator= is impossible.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622