我习惯于编写带有文件名或从std::cin
读取的小命令行工具,所以我使用这种模式已经有一段时间了:
int main(int argc, char* argv[])
{
std::string filename;
// args processing ...
std::ifstream ifs;
if(!filename.empty())
ifs.open(filename);
std::istream& is = ifs.is_open() ? ifs : std::cin;
std::string line;
while(std::getline(is, line))
{
// process line...
}
return 0;
}
在阅读有关Stack Overflow的问题后,我尝试修改我常用的模式,以满足从文件或std::istringstream
中读取的需要。令我惊讶的是它不会编译并给出这个错误:
temp.cpp:164:47: error: invalid initialization of non-const reference of type ‘std::istream& {aka std::basic_istream<char>&}’ from an rvalue of type ‘void*’ std::istream& is = ifs.is_open() ? ifs : iss; // won't compile
我认为它试图将std::istringstream
对象(iss
)转换为布尔值并获取其operator void*()
。
int main(int argc, char* argv[])
{
std::string filename;
std::string data;
// args processing ...
std::ifstream ifs;
std::istringstream iss;
if(!filename.empty())
ifs.open(filename);
std::istream& is = ifs.is_open() ? ifs : iss; // won't compile
std::string line;
while(std::getline(is, line))
{
// process line...
}
return 0;
}
为什么std::istringstream
与std::cin
和std::ifstream
的处理方式不同?它们都来自std::istream
。
然后我记得转换了我的模式以适应三种可能性,从文件,字符串或std::cin
读取。我记得那很有效(尽管它非常笨拙)。因此,对这个问题应用三重解决方案,我想出了一个完全有效的软糖:
int main(int argc, char* argv[])
{
std::string filename;
std::string data;
// args processing ...
std::ifstream ifs;
std::istringstream iss;
if(!filename.empty())
ifs.open(filename);
std::istream& is = ifs.is_open() ? ifs : true ? iss : std::cin; // fudge works
std::string line;
while(std::getline(is, line))
{
// process line...
}
return 0;
}
为什么这个软糖有效? GCC是否违反了关于三元运算符(?:
)如何解析其类型的规则?或者我错过了什么?
答案 0 :(得分:14)
如果您有基类和派生类,则三元条件运算符知道将派生类转换为基类。但是如果你有两个派生类,它们不知道将它们转换为它们的公共基类。这不是gcc的表现;这就是指定三元条件运算符如何在标准中工作。
std::istream& is = ifs.is_open() ? ifs : std::cin;
这很好,因为std::cin
的类型为std::istream
,std::ifstream
是基类。
std::istream& is = ifs.is_open() ? ifs : iss; // won't compile
这不起作用,因为std::ifstream
和std::istringstream
&#34;仅限&#34;有一个共同的基类。
std::istream& is = ifs.is_open() ? ifs : true ? iss : std::cin; // fudge works
这是有效的,因为它被解析为:
std::istream& is = ifs.is_open() ? ifs : (true ? iss : std::cin);
,带括号的表达式的类型为std::istream
。因此iss
会被转换为std::istream
类型的左值,如果选中,ifs
也会被转换为类似。
答案 1 :(得分:6)
编译器尝试从三元运算符中找到两个结果的公共类型,如果您看到例如this reference您会看到有一个casting operator override for void*
(or bool
for C++11 and later),因此编译器会使用它。
但是当它尝试执行赋值时,它会输出错误,因为在初始化的右侧,您有void*
(或者bool
)类型,并且在左侧这边是std::istream
的引用。
要解决此问题,您必须手动将每个流投射到std::istream
的引用,例如static_cast
。
答案 2 :(得分:1)
gcc抱怨,因为ifs和iss是两种不同的类型。 static_casting类型为std :: istream&amp;将解决您的问题。