在重载的 << 运算符中使用 << 运算符
Using a << operator inside an overloaded << operator
我正在编写一个 class 重载 << 和 >> 运算符,类似于 std::istream 和 std::ostream。前几个功能按预期工作。但是,我想在我自己的 class 中使用其他 std::*stream classes 进行格式化,这会导致一个有趣的 'recursive' 编译器问题:
class SmartStream
{
public:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
in.m_buf << f; // This causes a 'recursive' compile call
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
实际错误很长,但它是这样开始的:
rec_stream.cpp: In instantiation of 'F& operator<<(F&, float) [with F = std::basic_ostringstream<char>]':
rec_stream.cpp:14:18: required from 'F& operator<<(F&, float) [with F = ExtraStream]'
rec_stream.cpp:60:11: required from here
rec_stream.cpp:12:9: error: 'class std::basic_ostringstream<char>' has no member named 'type'
in.type ("f");
^
显然,编译器将相同的重载 << 运算符应用于 std::ostringstream 类型的 m_buf
变量,但当然它没有 type() 和 add() 函数。阅读 this question 的答案后,有点道理,但没有提供解决方案。
不在你说之前,在 SmartStream 中使用这个:
class SmartStream
{
....
SmartStream &operator << (float f)
{
type("f");
m_buf.str ("");
m_buf << f; // Ambiguous overload
add( m_buf.str ());
return *this;
}
};
有两个问题。首先,如代码中所述,这会触发不明确的过载。其次,考虑以下几点:
class ExtraStream: public SmartStream
{
public:
template <typename F> friend F &operator << (F &in, struct tm t)
{
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour; // Ambiguous overload
in.add (in.m_buf.str ());
return in;
}
};
事实上,我正在扩展 SmartStream 以使用 class 中规定的机制来处理自定义类型。使用非好友运算符将不允许我这样做:
ExtraStream es;
struct tm t1, t2;
float f1, f2;
es << f1 << t1 << f2 << t2; // works
因为在 << (float)
之后 return 类型是 SmartStream,它不知道如何处理 struct tm
s。
我的问题:
有没有办法说服编译器 (gcc 4.8.2) 对 std::ostringstream 使用 'base' << 运算符而不是重载运算符?我尝试了各种转换,:: 解析运算符,将带有 in.m_buf << f;
的代码移动到 SmartStream 中的非模板函数,但没有任何帮助。
另外,为什么它只在模板运算符 << 函数中使用 this?在该函数之外对 std::ostringstream 的任何 << 使用都按预期工作。
所以,我认为最简单的方法是使用 std::enable_if
之类的东西来限制友元函数,以便它们仅在具有正确父级 class.[=12= 的流上有效]
#include <sstream>
#include <type_traits>
class SmartStream {
public:
template <typename F>
friend typename std::enable_if<std::is_base_of<SmartStream, F>::value, F&>::type
operator << (F &in, float f) {
in.type("f");
in.m_buf.str("");
in.m_buf << f;
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
class ExtraStream: public SmartStream {
public:
template <typename F>
friend typename std::enable_if<std::is_base_of<ExtraStream, F>::value, F&>::type
operator << (F &in, struct tm t) {
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour;
in.add (in.m_buf.str ());
return in;
}
};
int main() {
SmartStream ss;
ExtraStream es;
struct tm t;
ss << 3.14f;
es << 3.14f << t << 3.14;
// ss << t; // Fails as expected.
}
我在 gcc buglist 上发布了这个,因为我觉得这是一个错误,或者至少是 C++ 参考中的一个歧义。 Jonathan Wakely 提出了一个非常简单的解决方案:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
using std::operator <<; // indicate proper operator in the next line
in.m_buf << f;
in.add (in.m_buf.str ());
return in;
}
所以它确实需要一个 using
子句,只是我不希望的一个子句。
我正在编写一个 class 重载 << 和 >> 运算符,类似于 std::istream 和 std::ostream。前几个功能按预期工作。但是,我想在我自己的 class 中使用其他 std::*stream classes 进行格式化,这会导致一个有趣的 'recursive' 编译器问题:
class SmartStream
{
public:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
in.m_buf << f; // This causes a 'recursive' compile call
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
实际错误很长,但它是这样开始的:
rec_stream.cpp: In instantiation of 'F& operator<<(F&, float) [with F = std::basic_ostringstream<char>]':
rec_stream.cpp:14:18: required from 'F& operator<<(F&, float) [with F = ExtraStream]'
rec_stream.cpp:60:11: required from here
rec_stream.cpp:12:9: error: 'class std::basic_ostringstream<char>' has no member named 'type'
in.type ("f");
^
显然,编译器将相同的重载 << 运算符应用于 std::ostringstream 类型的 m_buf
变量,但当然它没有 type() 和 add() 函数。阅读 this question 的答案后,有点道理,但没有提供解决方案。
不在你说之前,在 SmartStream 中使用这个:
class SmartStream
{
....
SmartStream &operator << (float f)
{
type("f");
m_buf.str ("");
m_buf << f; // Ambiguous overload
add( m_buf.str ());
return *this;
}
};
有两个问题。首先,如代码中所述,这会触发不明确的过载。其次,考虑以下几点:
class ExtraStream: public SmartStream
{
public:
template <typename F> friend F &operator << (F &in, struct tm t)
{
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour; // Ambiguous overload
in.add (in.m_buf.str ());
return in;
}
};
事实上,我正在扩展 SmartStream 以使用 class 中规定的机制来处理自定义类型。使用非好友运算符将不允许我这样做:
ExtraStream es;
struct tm t1, t2;
float f1, f2;
es << f1 << t1 << f2 << t2; // works
因为在 << (float)
之后 return 类型是 SmartStream,它不知道如何处理 struct tm
s。
我的问题:
有没有办法说服编译器 (gcc 4.8.2) 对 std::ostringstream 使用 'base' << 运算符而不是重载运算符?我尝试了各种转换,:: 解析运算符,将带有 in.m_buf << f;
的代码移动到 SmartStream 中的非模板函数,但没有任何帮助。
另外,为什么它只在模板运算符 << 函数中使用 this?在该函数之外对 std::ostringstream 的任何 << 使用都按预期工作。
所以,我认为最简单的方法是使用 std::enable_if
之类的东西来限制友元函数,以便它们仅在具有正确父级 class.[=12= 的流上有效]
#include <sstream>
#include <type_traits>
class SmartStream {
public:
template <typename F>
friend typename std::enable_if<std::is_base_of<SmartStream, F>::value, F&>::type
operator << (F &in, float f) {
in.type("f");
in.m_buf.str("");
in.m_buf << f;
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
class ExtraStream: public SmartStream {
public:
template <typename F>
friend typename std::enable_if<std::is_base_of<ExtraStream, F>::value, F&>::type
operator << (F &in, struct tm t) {
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour;
in.add (in.m_buf.str ());
return in;
}
};
int main() {
SmartStream ss;
ExtraStream es;
struct tm t;
ss << 3.14f;
es << 3.14f << t << 3.14;
// ss << t; // Fails as expected.
}
我在 gcc buglist 上发布了这个,因为我觉得这是一个错误,或者至少是 C++ 参考中的一个歧义。 Jonathan Wakely 提出了一个非常简单的解决方案:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
using std::operator <<; // indicate proper operator in the next line
in.m_buf << f;
in.add (in.m_buf.str ());
return in;
}
所以它确实需要一个 using
子句,只是我不希望的一个子句。