此代码有效:
std::ifstream f(mapFilename.c_str());
std::string s = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
ParseGameState(s);
其中mapFilename
是std::string
和void ParseGameState(const std::string&);
。
这不是:
std::ifstream f(mapFilename.c_str());
std::string s(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
ParseGameState(s);
这是错误:
game.cpp: In member function ‘int Game::LoadMapFromFile(const std::string&)’:
game.cpp:423: error: no matching function for call to ‘ParseGameState(std::string (&)(std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> > (*)()))’
game.cpp:363: note: candidates are: ParseGameState(const std::string&)
因此,在这种情况下,它似乎将s
识别为函数声明而不是变量声明。
为什么?这是GCC 4.2.1(Apple版本)中的错误吗?或者GCC是否正确处理了这个问题?这在C ++标准中是否未定义?
答案 0 :(得分:14)
这是C ++的“最令人烦恼的解析”。一个快速的Google应该会有大量的详细信息。基本答案是肯定的,编译器 将其视为函数声明 - 而C ++要求它这样做。你的编译器没有任何问题(至少在这方面)。
如果有任何安慰,那么你有很多好的公司参与其中。实际上,C ++ 0x添加了一个新的大括号初始化语法是很常见的,这在很大程度上是因为它避免了这种歧义。使用它,您可以编写如下内容:
std::string s{std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};
这将清楚地表明大括号的内容旨在成为将s
,而不是类型的参数初始化为名为s
的函数的值。我不知道Apple是否有它的端口,但是gcc接受了4.5版本(或左右)的新语法。
编辑:重读N3092,约翰内斯(照例)非常正确。适用的语言是(§8.5.4/ 3/5):“如果T有一个初始化列表构造函数,则参数列表由初始化列表作为单个参数组成;否则,参数列表由初始化程序的元素组成列表“。
因此,由于std::string
有一个初始化列表构造函数,这会尝试将两个istreambuf_iterator
“填充”到初始化列表中,并将其传递给std::string
ctor,采用初始化列表 - 但这将是类型不匹配,因此代码无法编译。对于某些其他类型的类型(与std::string
不同,不具有初始化列表ctor),上面的转换将起作用(感谢上面引用中的“否则......”)。对于std::string
,您必须使用当前替代方案之一,例如std::string s = std:string(...)
。
我为错误的建议解决方案道歉 - 在这种情况下,情况更糟,因为它混淆了一个可能过于混乱的问题,如果有什么需要仔细澄清,特别是在未来几年