error: no matching function for call to 'std::optional<smoltcp::Buffer>::optional(smoltcp::Buffer&)'

I am trying to make a function that returns an std::optional to Buffer:

std::optional<Buffer> receive(SmolSocket smolSocket)
{
   //...
   return std::optional<Buffer>(buffer);
}

Where Buffer is simply

struct Buffer
{
public:
   std::unique_ptr<uint8_t[]> data;
   size_t len = 0;
   bool empty = false;

   Buffer(CBuffer cBuffer)
   {
      data = std::unique_ptr<uint8_t[]>(cBuffer.data);
      len = cBuffer.len;
   }

   Buffer(bool empty)
   {
      data = std::unique_ptr<uint8_t[]>(nullptr);
      this->len = 0;
      this->empty = empty;
   }

   uint8_t* getData()
   {
      return data.get();
   }
};

But I am getting lots of errors:

/workspaces/libopenvpnclient/src/libopenvpn/../../smoltcp_cpp_interface/src/virtual_tun/interface.h:283:52: error: no matching function for call to 'std::optional<smoltcp::Buffer>::optional(smoltcp::Buffer&)'
  283 |                 return std::optional<Buffer>(buffer);

/usr/include/c++/9/optional:760:2: note: candidate: 'template<class ... _Args, typename std::enable_if<__and_v<std::is_constructible<smoltcp::Buffer, _Args&& ...> >, bool>::type <anonymous> > constexpr std::optional<_Tp>::optional(std::in_place_t, _Args&& ...)'
  760 |  optional(in_place_t, _Args&&... __args)
      |  ^~~~~~~~
/usr/include/c++/9/optional:760:2: note:   template argument deduction/substitution failed:
/usr/include/c++/9/optional:758:56: error: no type named 'type' in 'struct std::enable_if<false, bool>'
  758 |         _Requires<is_constructible<_Tp, _Args&&...>> = false>

note:   'smoltcp::Buffer' is not derived from 'std::optional<_Tp>'
  283 |                 return std::optional<Buffer>(buffer);

I have used std::optional in other situations and I can not see what is the problem here.

I also tried using return std::optional<Buffer>{buffer};.

2 answers

  • answered 2020-07-29 17:23 Ihor Drachuk

    To be short: You should add copy- or move-constructor to Buffer.

    More details:

    You have Buffer object and need to create another Buffer (in std::optional), but there is no constructor in Buffer which can accept parameters you're passing. Compiler doesn't generate default copy/move-constructors, because you declared custom one. So, you should construct std::optional<Buffer> from bool, from CBuffer or add another one with const Buffer& or Buffer&&.

  • answered 2020-07-29 17:35 JeJo

    The problem is, in order to do

    std::optional<Buffer>(buffer);
    

    you need a copy-or-move constructor. When you provide a parameterized constructor (i.e. Buffer(CBuffer cBuffer)), the compiler will not generate both of them. So you need to explicitly provide them.

    However, in your case, you have one more issue. The member data is non-copyable(i.e. std::unique_ptr<uint8_t[]>), meaning only move constructor is that you can provide.

    Therefore provide the move-constructor

    struct Buffer
    {
    public:
       Buffer(Buffer&& rhs)
          : data{ std::move(rhs.data) }
          , len{rhs.len}
       {}
       // ... other code
    };
    

    and move the buffer in the function:

    std::optional<Buffer> receive()
    {
       // ...
       return std::optional<Buffer>(std::move(buffer));
    }
    

    (See a minimal example demo)