4

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?

2
  • Which is line 16? Commented Jul 4, 2020 at 21:28
  • @ecatmur return std::make_optional(file); // results in compilation error is line 16. Commented Jul 4, 2020 at 21:30

2 Answers 2

10

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.)

Sign up to request clarification or add additional context in comments.

5 Comments

just return file ^^
Indeed, I'll add it to the answer, but it hides what's actually happening a bit.
i prefer the hiding and appreciate the "it just works". ymmv.
I think the long form serves as education, the short form as the form for actual code.
I'm using g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, and just returning the file results in a compilation error because it apparently doesn't implicitly convert the stream into an optional. Explicitly moving the stream into the make_optional does work however! Thank you.
3

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.