将临时对象转换为非常量引用时出错
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++ 的当前行为。
这是从关于 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++ 的当前行为。