Rasmus Källqvist
Rasmus Källqvist

Reputation: 332

Why doesn't make_optional work for file streams?

I'm trying out the C++17 optional-type, and thought a fitting place to use it would be a function that attempts to open a file and maybe returns the opened file. The function I wrote looks like this:

std::optional<std::fstream> openFile(std::string path)
{
    std::fstream file;
    file.open(path);
    if (!file.is_open())
    {
        std::cerr << "couldn't open file" << path << std::endl;
        return {};
    }
    else
    {
        return std::make_optional(file); // results in compilation error
    }
}

But when I try to compile this with g++ with -std=c++17 as one of the arguments I get a big wall of template compilation error messages, starting with:

In file included from read_file.cpp:3:0:
/usr/include/c++/7/optional: In instantiation of ‘constexpr std::optional<typename std::decay<_Tp>::type> std::make_optional(_Tp&&) [with _Tp = std::basic_fstream<char>&; typename std::decay<_Tp>::type = std::basic_fstream<char>]’:
read_file.cpp:16:39:   required from here
/usr/include/c++/7/optional:991:62: error: no matching function for call to ‘std::optional<std::basic_fstream<char> >::optional(<brace-enclosed initializer list>)’
     { return optional<decay_t<_Tp>> { std::forward<_Tp>(__t) }; }

Why would it seem as if fstream can't be used with std::optional? Am I approaching this in the wrong way? If optional doesn't support stream-types, does that not limit where the type can be applied?

Upvotes: 4

Views: 606

Answers (2)

Bert
Bert

Reputation: 418

Your code will try and copy the stream, when you pass it to make_optional. Streams can't be copied, therefore, you need to move it, i.e.,

return std::make_optional(std::move(file));

or simply

return file;

(Depending on the age of the compiler, the latter might not work.)

Upvotes: 10

NathanOliver
NathanOliver

Reputation: 180500

std::make_optional calls the optional constructor with the form of

template < class U = value_type >
constexpr optional( U&& value );

and that constructor behaves as doing

T optional_data = std::forward<U>(value)

which is going to make a copy since you passed an lvalue. Streams are not copyable, so you get an error. You have to move the stream into the optional to get it to work correctly.

Upvotes: 3

Related Questions