将临时对象转换为非常量引用时出错

Error when casting temporary object to non-const reference

这是从关于 using temporary stringstream object:

的问题中提取的可重现示例
#include <sstream>                     
#include <string>
#include <iostream>

using namespace std;

std::string transform(std::string);

int main()
{
    int i{};
    cout << transform( static_cast<stringstream &>(stringstream() << i).str() );
}

在 MacOS High Sierra 下尝试使用 clang 版本 9.0.0 进行编译时出现以下错误:

$ clang++ -std=c++11 x.cc -c
x.cc:12:24: error: non-const lvalue reference to type 'basic_stringstream<...>' cannot bind to a temporary of type 'basic_stringstream<...>'
    cout << transform( static_cast<stringstream &>(stringstream() << i).str() );
                       ^                           ~~~~~~~~~~~~~~~~~~~
1 error generated.

当在同一台机器上(也在 Linux 上)使用 g++ 9.2.0 时,一切都可以正常编译。

似乎将转换从 stringstream & 更改为 const stringstream &stringstream && 可以解决问题。

问题是这是编译器错误还是 clang 对某些标准规则更严格?

答案是 libstdc++ 和 libc++ 之间 ostream 插入的不同实现。

实现几乎相同,但libc++实现中有以下代码(https://github.com/llvm/llvm-project/blob/master/libcxx/include/ostream):

template <class _Stream, class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
typename enable_if
<
    !is_lvalue_reference<_Stream>::value &&
    is_base_of<ios_base, _Stream>::value,
    _Stream&&
>::type
operator<<(_Stream&& __os, const _Tp& __x)
{
    __os << __x;
    return _VSTD::move(__os);
}

这会获取您在原位创建的右值 strstream 和 returns 一个右值,左值引用无法绑定到该右值。

另一方面,libstdc++ implementation 具有以下内容:

 template<typename _Ostream>
    using __rvalue_ostream_type =
      typename __is_convertible_to_basic_ostream<
    _Ostream>::__ostream_type;


  template<typename _Ostream, typename _Tp>
    inline
    typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>,
                  __is_convertible_to_basic_ostream<_Ostream>,
                  __is_insertable<
                __rvalue_ostream_type<_Ostream>,
                const _Tp&>>::value,
               __rvalue_ostream_type<_Ostream>>::type
    operator<<(_Ostream&& __os, const _Tp& __x)
    {
      __rvalue_ostream_type<_Ostream> __ret_os = __os;
      __ret_os << __x;
      return __ret_os;

其中 __rvalue_ostream_type 在一些魔法之后变成不是 r 值参考。因此代码编译。

以上是libstdc++的bug,可以作为bug报告提交。

是的,我认为这是 libc++ 中的错误。

根据 [ostream.rvalue] 有过载:

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

But libc++ implements this similar to:

template <class _Stream, class _Tp>
enable_if_t</*...*/, _Stream&&>
operator<<(_Stream&& __os, const _Tp& __x)
{
    // ...
}

如果流右值与 << 一起使用,则该实现使此重载成为比 ostream 的 in-class operator<< 更好的候选者,而标准中的签名不会,它也是 return 右值引用,而它应该 return 左值引用。它还 return 是与传递给它的相同类型的引用,而根据标准引用,它应该 return 对其 ostream 基 class 的引用。

右值引用不能绑定到非常量左值引用,因此出现错误。

该错误已被报告 here and there is an open LWG issue regarding the behavior here,这似乎表明将来可能会调整标准以强制执行 libc++ 的当前行为。