Why can I use `operator<<` on temporary std::ofstream objects?
Asked Answered
C

2

7

According to the C++ standard you cannot bind a temporary to a non-const reference. Since the stream output operator is defined as

template <class CharT, class Traits, class Allocator>

std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& os,
               const std::basic_string<CharT, Traits, Allocator>& str);

I would expect it to not be callable on temporary stream objects. However, I tried the following and got unexpected results

#include <fstream>

std::ostream& print(std::ostream &stream) {
    stream << "test\n";
    return stream;
}

int main() {
    std::fstream("") << "test\n";
    // print(std::fstream("")); // Doesn't compile, as expected
}

This compiles on GCC trunk, Clang trunk and MSVC 19. I even tried -pedantic-errors on the first two. While technically possible that all three are wrong, it is likely that I am misunderstanding something.

Can somebody find a definitive answer in the standard on whether this is legal C++ or not?

Carbo answered 10/9, 2019 at 9:21 Comment(0)
D
7

The C++ standard mandates the following function template existing (C++17 n4659 30.7.5.5 [ostream.rvalue]):

template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);

With effects specified as os << x.

Note that the same exists for extraction (>>) as well.

Denaedenarius answered 10/9, 2019 at 9:24 Comment(2)
Interesting. I was mislead by this page into assuming there was no r-value reference version. In fact, I even verified that the l-value version I posted was called in the assembly of gcc and clang. Obviously it was because of inlining but it reinforced my misunderstanding.Carbo
@Carbo To be fair, there indeed is no rvalue version specific for std::basic_string, so the page is correct. This generic template covers all types, though.Denaedenarius
J
9

There is overload which takes stream by Rvalue reference:

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
                                        const T& value );

temp is passed as os. From reference.

Jello answered 10/9, 2019 at 9:24 Comment(0)
D
7

The C++ standard mandates the following function template existing (C++17 n4659 30.7.5.5 [ostream.rvalue]):

template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);

With effects specified as os << x.

Note that the same exists for extraction (>>) as well.

Denaedenarius answered 10/9, 2019 at 9:24 Comment(2)
Interesting. I was mislead by this page into assuming there was no r-value reference version. In fact, I even verified that the l-value version I posted was called in the assembly of gcc and clang. Obviously it was because of inlining but it reinforced my misunderstanding.Carbo
@Carbo To be fair, there indeed is no rvalue version specific for std::basic_string, so the page is correct. This generic template covers all types, though.Denaedenarius

© 2022 - 2024 — McMap. All rights reserved.