I have the following piece of code
// DoSomething.h
class Foo;
void doSomething(std::optional<std::unique_ptr<const Foo>> f = std::nullopt);
If I include DoSomething.h anywhere without the definition of Foo the file doesn't compile, even if it doesn't even call the doSomething function. In contrast, removing the =std::nullopt default argument, everything compiles fine.
My assumption is the issue has something to do with the deleter interface of the unique_ptr, and my question is how to solve this issue so I can forward declare Foo in this scenario?
Thanks
Edit: I don't want to change the signature of this function, I know I can solve this with overloading or removing the optional use. I want to understand why this doesn't work.
Following note - this same code compiles perfectly fine when switching the std::unique_ptr with std::shared_ptr. It means it should be possible to make it work with std::unique_ptr. I know their deleteres have slightly different interface, and I'm not familiar enough with it.
Cppreference says the following about the template argument of
std::optional:std::unique_ptr<T>is not destructible whenTis incomplete.The default arguments of a function are instantiated at the declaration of that function.
Therefore the requirements of
std::optionalare not satisfied at the point where the object is instantiated.As noted in the comments, you can solve the issue by overloading the function instead of using default arguments. Also pointers expose a null value, so in most cases
std::optional<pointer-type>is not necessary.Follow up after reading your edit:
This code will compile fine. It uses
std::unique_ptrwith a type erased deleter similar tostd::shared_ptr. The problem if you instantiateunique_ptrwith the default deleter (std::default_delete) is this: A simplified definition ofstd::default_deletewould look something like this:So during the instantiation of
std::optional<std::unique_ptr<Foo>>the code above will be instantiated. Because it makes a call to the destructor ofFoothat is not declared compilation will fail. If you instantiatestd::optional<std::unique_ptr<Foo, FooDeleter>>, onlyoperator()ofstd::function<...>will be called, which is independent of the definition of the destructor ofFoo.