为什么重载运算符 '<<' 上的异常说明符对任何 std::ostream 对象不起作用,但对库中定义的对象起作用?

Why exception specifiers on overloaded operators '<<' doesn't work to any std::ostream object, but does on those defined in the library?

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;
}

编译命令:

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

错误:

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
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~

给定类型的 'declval()' 函数 "generates a pseudo compile time object",即使这种类型实际上不是可构造的。所以 'declval()' 应该产生另一个像 std::cout 这样的对象,其中第 21 行中的重载运算符应该在编译时工作(但它没有)。

我意识到这也适用于 std::clog 和 std::cerr,这两个都是 ostream 类型的变量。

它应该可以编译。我的意思是异常说明符应该与任何 ostream 对象相同,显然不仅仅是这三个对象。

注意:使用G++ 8.1.0编译;不需要图像上的标志。实际上,没有标志或只有标志 -std=c++11 或更高版本可能会给出相同的输出。

原因是declval生成一个临时对象,所以如果你的代码有另一个重载,像这样

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

它会起作用的。请注意,重载函数采用右值引用。我用 gcc 4.8.5 和 -std=c++11.

测试了它

您不能将纯右值 ostream 绑定到 ostream &,而应该使用 ostream & 表达式。

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