for*_*818 28 c++ operator-overloading
我一直在寻找一种解决方案来同时写入文件和控制台.我发现了一个很好的解决方案在这里.
当我在C++ 11之前工作时,我不得不对Orbit中Lightness Races的代码进行一些小改动:
#include <iostream>
#include <fstream>
#include <string>
struct OutputAndConsole : std::ofstream
{
OutputAndConsole(const std::string& fileName)
: std::ofstream(fileName.c_str()) // constructor taking a string is C++11
, fileName(fileName)
{};
const std::string fileName;
};
template <typename T>
OutputAndConsole& operator<<(OutputAndConsole& strm, const T& var)
{
std::cout << var;
static_cast<std::ofstream&>(strm) << var;
return strm;
};
Run Code Online (Sandbox Code Playgroud)
它很好地工作除了一个小东西需要困惑我.如果我像这样使用它:
int main(){
OutputAndConsole oac("testLog.dat");
double x = 5.0;
oac << std::endl;
static_cast<OutputAndConsole&>(oac << "foo \n" << x << "foo").operator<<(std::endl);
oac << "foo" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
然后,std::endl当它们在文件中正确显示时,控制台上的输出将忽略所有内容.我的猜测是,当我使用std::endl了ostream::operator<<被称为将打印到文件而不是控制台.与static_cast<OutputAndConsole&>我一起尝试调用正确的运算符,但仍然只有\n控制台上出现换行符.
为什么要std::endl调用错误的运算符?
我怎么称呼正确的?
PS:我知道我可以\n毫无问题地使用,但我仍然想知道这里发生了什么,以及如何解决它.
Yak*_*ont 12
struct OutputAndConsole : std::ofstream
{
// ...
};
template <typename T>
OutputAndConsole& operator<<(OutputAndConsole& strm, const T& var);
Run Code Online (Sandbox Code Playgroud)
正如其他人所提到的,std::endl是模板功能.模板函数不是值,它只是一个名称.
如果您尝试将模板函数传递给期望兼容签名功能的函数,则可以将模板函数转换为值.这是不是如果传递给一个模板函数回吐转换为值T或const T&,因为模板函数的名称代表了整个主机的可能值.
因为std::endl它不是您自定义编写的有效参数operator<<,所以它在其他地方查找.它发现std::ofstream的operator<<,通过明确的函数指针需要一个IO控制器函数.
那一个工作,它可以转换endl为该函数指针类型!所以,兴高采烈地,它称之为.
要解决此问题,请添加一个operator<<重载OutputAndConsole,以获取io操纵器函数指针.
最简单的方法是编写辅助函数:
template <class T>
void output_to(OutputAndConsole& strm, const T& var)
{
std::cout << var;
static_cast<std::ofstream&>(strm) << var;
};
Run Code Online (Sandbox Code Playgroud)
然后两个<<重载:
template<class T>
OutputAndConsole& operator<<(OutputAndConsole& strm, const T& var) {
output_to(strm, var);
return strm;
}
OutputAndConsole& operator<<(OutputAndConsole& strm, std::ostream& (*var)(std::ostream&)) {
output_to(strm, var);
return strm;
}
Run Code Online (Sandbox Code Playgroud)
这导致std::endl模板找到匹配<<.
use*_*264 11
让我们尝试一些更简单的方法:
#include <iostream>
struct Foo { };
template <typename T>
Foo& operator<<(Foo& foo, const T& var)
{
std::cout << var;
return foo;
};
int main(){
Foo foo;
foo << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
这不编译:
a1.cpp: In function ‘int main()’:
a1.cpp:14:9: error: no match for ‘operator<<’ (operand types are ‘Foo’ and ‘<unresolved overloaded function type>’)
foo << std::endl;
^
a1.cpp:14:9: note: candidates are:
a1.cpp:6:6: note: template<class T> Foo& operator<<(Foo&, const T&)
Foo& operator<<(Foo& foo, const T& var)
^
a1.cpp:6:6: note: template argument deduction/substitution failed:
a1.cpp:14:17: note: couldn't deduce template parameter ‘T’
Run Code Online (Sandbox Code Playgroud)
为什么?编译器试图告诉我们什么?
std :: endl的定义可以在这里找到:http: //en.cppreference.com/w/cpp/io/manip/endl
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );
Run Code Online (Sandbox Code Playgroud)
所以std::endl不是变量.这是一个模板.更准确地说,是模板功能.在我的小代码中,编译器无法实例化模板.
当我们直接调用std::cout << std::endl;,编译器实例化std::endl从CharT和Traits的decltype(std::cout).
在您的代码中,编译器使用CharT和Traits从实例化模板std::ofstream,因为您OutputAndConsole是.的后代std::ofstream.当std::cout尝试输出错误的实例时std::endl,它会失败.
PS:最后一段只是部分正确.当你写作
oac << something;
Run Code Online (Sandbox Code Playgroud)
哪里something有T型,
从理论上讲,它可以称为两个中的任何一个
std::ofstream& std::ofstream::operator<<(T) // or operator<<(const T&)
// -- or --
OutputAndConsole& operator<<(OutputAndConsole& strm, const T& var);
Run Code Online (Sandbox Code Playgroud)
第一个定义是可能的,因为OutputAndConsole继承的成员函数operator<<从std::ofstream.第二种形式由您提供.
何时something是变量,它使用第二个定义.
何时something是模板,它不能使用第二个定义,因为无法确定模板的参数.所以它使用了第一个定义.因此,
oac << std::endl; // std::endl is a template
Run Code Online (Sandbox Code Playgroud)
相当于
static_cast<ofstream&>(oac) << std::endl;
Run Code Online (Sandbox Code Playgroud)
我们可以通过以下代码看到它:
#include <iostream>
struct Foo : std::ofstream {};
template <typename T>
Foo& operator<<(Foo& strm, const T& var)
{
std::cout << "X" << std::endl;
return strm;
};
int main() {
Foo oac;
oac << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
此代码不打印"X".
std::endl是一个函数,而不是一个字符串.你的重载方法需要一个字符串来进行重载,所以当你这样做的时候不会被调用<< std::endl
您需要创建一个运算符,该运算符采用具有相同签名的函数std:endl来执行重载.
std::ostream& operator<<( std::ostream& (*f)(std::ostream&) )
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2144 次 |
| 最近记录: |