编译以下重载<<需要什么enable_if或其他提示?

What enable_if or other hint is need for the following overloaded << to compile?

本文关键字:lt if enable 提示 其他 重载 编译 什么      更新时间:2023-10-16

允许<lt;超负荷工作?t.cxx是:

#include <sstream>
#include <string>
struct St : std::ostringstream {
template<typename Tp> St& operator<<( const Tp& value ) { return *this; }
operator std::string() const { return str(); }
friend std::ostream& operator<<( std::ostream& s, const St& ss ) { return 
s << ss.str(); }
};
struct Memory_type {
std::string to_string() const { return St() << "s=" << s; }
const char* s;
};

g++t.cxx

t.cxx: In member function 'std::__cxx11::string Memory_type::to_string() const':
t.cxx:10:46: error: ambiguous overload for 'operator<<' (operand types are 'St' and 'const char [3]')
std::string to_string() const { return St() << "s=" << s; }
~~~~~^~~~~~~
t.cxx:5:28: note: candidate: St& St::operator<<(const Tp&) [with Tp = char [3]]
template<typename Tp> St& operator<<( const Tp& value ) { return *this; }
^~~~~~~~

问题是,由于C++11有一个operator<<重载可用于右值的ostream.rvalue]。这会导致歧义,因为St() << "s="可以调用St::operator<<重载或[ostream.ervalue]重载。这可以通过使St::operator<<重载仅对lvalues有效来解决,方法是向其添加&ref限定符:

template<typename Tp> St& operator<<( const Tp& value ) &

然而,这恰恰揭示了另一个问题,即表达式return St() << "s=" << s;假设<<运算符将返回原始类型,而不是std::ostream&基类(这样它就可以隐式地从St转换为std::string,这似乎是一个有问题的设计(。返回派生类型不是ostream操作通常的工作方式。对ostream对象的操作通常返回对基类的引用,而不是传递给它的派生类型

这个问题可以通过使用左值对象来解决,而不是试图将其全部写为右值上的单个表达式:

St st;
st << "s=" << s;
return st;

这是有效的,因为return st;看到的是原始类型,而不仅仅是ostream基类。

这是GCC中的一个错误。相比之下,代码在clang和Visual Studio上运行得非常完美。

编辑:显然,根据标准(见评论(,GCC是正确的。

您的冲突是由于GCC在这种情况下处理右值引用的方式造成的。GCC有以下定义:

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)
{ .... }

该定义将导致可转换为std::ostream的任何右值引用的歧义。作为一种变通方法,您可以停止使用std::ostream的右值引用,而是编写:

struct Memory_type {
std::string to_string() const 
{ 
// Defining a variable avoids passing an rvalue references of St
St st;
return st << "s=" << s; 
}
const char* s;
};

这应该适用于所有现代编译器。