Home > Mobile >  How to construct a std::optional of a struct with std::shared_ptr
How to construct a std::optional of a struct with std::shared_ptr

Time:10-11

I'm trying to create an optional with a class that has a shared_ptr but the implicitly deleted constructor prevents me from using make_optional to create it.

Is the only option to manually write that constructor?

Here is a simple reproduction:

https://onlinegdb.com/DcWzF1NAt

#include <optional>
#include <memory>
class A
{
public:
 A(int v):
  val{v}
  {}
 int val;   
};

class B
{
public:
 B(std::shared_ptr<A>& a):
   a{a}
 {}
 
 const std::shared_ptr<A> a;   
};


int main()
{
  std::shared_ptr<A> a = std::make_shared<A>(3);
  
  std::optional<B> b;
  b = std::make_optional<B>(a);
}

causes this compile error

main.cpp:31:30: error: use of deleted function ‘std::optional& std::optional::operator=(std::optional&&)’
   b = std::make_optional<B>(a);
                              ^
In file included from main.cpp:4:0:
/usr/include/c  /7/optional:453:11: note: ‘std::optional& std::optional::operator=(std::optional&&)’ is implicitly deleted because the default definition would be ill-formed:
     class optional
           ^~~~~~~~
/usr/include/c  /7/optional:453:11: error: use of deleted function ‘std::_Enable_copy_move& std::_Enable_copy_move::operator=(std::_Enable_copy_move&&) [with _Tag = std::optional]’
In file included from /usr/include/c  /7/optional:43:0,
                 from main.cpp:4:
/usr/include/c  /7/bits/enable_special_members.h:246:5: note: declared here
     operator=(_Enable_copy_move&&) noexcept                         = delete;
     ^~~~~~~~

CodePudding user response:

It's not an implicitly deleted constructor causing a problem, it's the implicitly deleted copy assignment operator and has nothing to do with std::shared_ptr, it's because you've declared the B::a member to be const. The solution here is to not mark it as const:

class B
{
public:
 B(std::shared_ptr<A>& a):
   a{a}
 {}

 std::shared_ptr<A> a;   
};

The reason the compiler deletes the copy assignment operator is because it has to be able to modify that member; we can see this with a slightly simpler example:

class NoCopyAssign {
 public:
  NoCopyAssign(int a) : mA(a) {}
 private:
  const int mA;
 };

Ordinarily, the copy assignment operator would be roughly equivalent to this:

NoCopyAssign &operator=(const NoCopyAssign &other) {
  mA = other.mA;
  return *this;
}

But this is impossible because mA is a const int, so you can't change its value. };

  • Related