1

exemplo.cpp:

#include <type_traits>
using std::is_same;

#include <utility>
using std::declval;

#include <iostream>
using std::ostream;
using std::cout;

struct Foo final {
    int value;
    inline constexpr Foo(const int i) noexcept : value{i} {};
    inline ~Foo() = default;
};

ostream& operator<<(ostream& out, const Foo& foo) noexcept { return out << foo.value; }

int main() {
    const Foo a(42);
    static_assert(is_same<decltype(cout), ostream>::value == true, ""); // assert always to true...

    static_assert(noexcept(cout << a) == true, ""); // assert to true if the operator on line 21 is defined noexcept(true)

    static_assert(noexcept(declval<ostream>() << a) == true, ""); // assert always to false...

    static_assert(noexcept(declval<decltype(cout)>() << a) == true, ""); // Same as line 32...

    return 0;
}

Compile command:

g++ -std=c++2a -fconcepts exemplo.cpp -o exemp.run

Errors:

exemplo.cpp:32:53: error: static assertion failed
    static_assert( noexcept( declval<ostream>() << a) == true, ""); // assert always to false...
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
exemplo.cpp:34:60: error: static assertion failed
    static_assert( noexcept( declval<decltype(cout)>() << a) == true, ""); // same as line 32
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~

The 'declval()' function "generates a pseudo compile time object" of a given type, even if this type isn't really contructible. So 'declval()' should produce another object like std::cout, in which the overloaded operator in line 21 should work at compile time (but it doesn't).

I realised that is also works to std::clog and std::cerr, both variable of ostream type.

It should just compile. I mean the exception especifier should be the same to any ostream object, not only for these three obviously.

Note: compiling with G++ 8.1.0; the flags on the image are not needed. Actually, no flag or only the flag -std=c++11 or greater may give the same output.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62

2 Answers2

0

The reason is that declval generate a temp object, so if you code has another overload, like this

ostream& operator<<(ostream&& out, const Foo& foo) noexcept { return out << foo.value; }

it will work. Note that the overloaded function takes rvalue reference. I tested it with gcc 4.8.5 and -std=c++11.

CS Pei
  • 10,869
  • 1
  • 27
  • 46
  • Thank you. I didn't forget declval() returns T&& (rvalue reference), but I thought once there's no other similar overload, it would work, or at least generate a different compile error saying some thing like: no function to call with such signature. – CppProgrammer Mar 24 '19 at 02:01
0

You can't bind a prvalue ostream to a ostream &, instead you should have an ostream & expression.

int main() {
    const Foo a(42);
    static_assert(is_same<decltype(cout), ostream>::value == true, ""); 

    static_assert(noexcept(cout << a) == true, "");

    static_assert(noexcept(declval<ostream&>() << a) == true, "");

    static_assert(noexcept(declval<decltype(cout)&>() << a) == true, ""); 

    return 0;
}

See it live

Caleth
  • 52,200
  • 2
  • 44
  • 75