C++包展开赋值tuple触发sequence-point警告是GCC漏洞还是Clang漏报?
问题相关代码
int print(std::string str, int i){ std::cout << i << str << std::endl; return i; } template<typename ... Args> void concat(Args ... args) { int i = 2; std::tuple<int, int, int, int> ret { print("ss", -22), print(args, i++) ... }; // gcc warning std::vector<int> ret1 { print("ss", -22), print(args, i++) ... }; } int main() { concat("a", "b", "c"); }
GCC编译时对tuple初始化行抛出如下警告:
warning: operation on 'i' may be undefined [-Wsequence-point]
结论
这是GCC的已知bug,属于误报,Clang的处理是正确的,没有遗漏风险。
规则与场景分析
C++11及之后的标准明确规定:花括号初始化列表(braced-init-list)中所有初始化子句的求值严格按从左到右的顺序执行,前一个子句的所有副作用完全生效后,才会执行下一个子句,不存在求值顺序不确定的问题。
- 针对vector初始化行:调用vector的
std::initializer_list构造函数,完全符合列表初始化的顺序规则,i++按从左到右依次执行,不存在未定义行为,所以所有编译器都不会报警告。 - 针对tuple初始化行:本质上同样是花括号列表初始化,也适用上述顺序规则,
i++的执行顺序是完全确定的,不存在未定义行为。
GCC误报的原因是它没有正确识别tuple的列表初始化场景,错误地将大括号内的元素当成了普通函数调用的实参。而C++17之前,普通函数调用的多个实参的求值顺序是未指定的,如果多个实参同时修改同一个变量确实存在未定义行为风险,GCC基于这个错误判断抛出了警告,属于已知的缺陷。
内容的提问来源于stack exchange,提问作者tejas
相关产品推荐
相关产品推荐

