为何std::string的第一个参数需要用括号包裹?
解析std::string构造时的括号歧义问题
先看错误场景和核心问题
你的错误代码大概是这样:std::ifstream vertex_shader_stream("shader.vert"); std::string s(std::istreambuf_iterator<char>(vertex_shader_stream), std::istreambuf_iterator<char>()); const char* ptr = s.c_str(); // 编译报错这里编译器根本没把
s当成std::string对象,而是把整行解析成了函数声明——这就是C++里臭名昭著的「最令人头疼的解析」(Most Vexing Parse)规则。编译器到底怎么解析的?
按照C++语法优先级,当语句可以被解析为函数声明时,编译器会优先这么做。具体到你的代码:s被解析成一个函数名,返回类型是std::string- 第一个参数:
std::istreambuf_iterator<char>(vertex_shader_stream)被等价解析为std::istreambuf_iterator<char> vertex_shader_stream——和你写int(x)等价于int x是一个道理,这是参数声明的写法 - 第二个参数:
std::istreambuf_iterator<char>()被解析成一个指向「无参、返回std::istreambuf_iterator<char>类型的函数」的指针
这下就明白为什么调用
s.c_str()会报错了——s是函数,不是字符串对象,自然没有c_str()成员。加括号为什么能解决?
当你把第一个参数用括号包裹成(std::istreambuf_iterator<char>(vertex_shader_stream))时,这个结构变成了一个表达式,而不是参数声明。C++语法里,括号包裹的内容无法被解析为参数声明,所以编译器只能把整行解析为std::string对象的初始化:用两个迭代器(文件流的起始和结束迭代器)构造字符串。其他替代写法
除了加括号,还有两种常见的规避方式:- C++11及以后用列表初始化:
列表初始化的语法不会触发函数声明歧义。std::string s{std::istreambuf_iterator<char>(vertex_shader_stream), std::istreambuf_iterator<char>()}; - 提前定义迭代器变量:
这种写法最直观,完全避免了语法歧义。std::istreambuf_iterator<char> stream_begin(vertex_shader_stream); std::istreambuf_iterator<char> stream_end; std::string s(stream_begin, stream_end);
- C++11及以后用列表初始化:
内容的提问来源于stack exchange,提问作者flakes
相关产品推荐
相关产品推荐

