GCC 5.5下std::tuple初始化编译报错的原因及规避方案
GCC 5.5无法编译嵌套tuple初始化列表?原因与修复方案
问题重现
你遇到的这个编译错误是GCC 5.x系列特有的兼容性问题,我之前维护老项目时也碰到过。先把问题代码和报错信息整理清楚:
std::vector< std::tuple<std::vector<std::size_t>, std::vector<std::size_t>, std::vector<std::size_t>> > foo = { {{2, 1, 2, 3}, {1, 2}, {2, 3}}, {{2, 3, 4, 0}, {3}, {2, 3, 4}}, {{2, 3, 4, 0}, {0}, {3, 4, 0}}, };
GCC 5.5的报错信息如下:
In function 'int main()': :16:4: error: converting to 'std::tuple<std::vector<long unsigned int, std::allocator<long unsigned int> >, std::vector<long unsigned int, std::allocator<long unsigned int> >, std::vector<long unsigned int, std::allocator<long unsigned int> > >' from initializer list would use explicit constructor 'constexpr std::tuple< <template-parameter-1-1> >::tuple(const _Elements& ...) [with _Elements = { std::vector<long unsigned int, std::allocator<long unsigned int> >, std::vector<long unsigned int, std::allocator<long unsigned int> >, std::vector<long unsigned int, std::allocator<long unsigned int> >}]' }; ^
为什么会报错?
这个问题的核心是GCC 5.x对C++初始化规则的实现限制:
std::tuple的多参数构造函数是explicit的(符合C++11及以后的标准)。当你用嵌套初始化列表初始化vector中的tuple元素时,编译器需要把内层的{{...}, {...}, {...}}隐式转换成std::tuple对象。但GCC 5.5严格遵循了早期的标准解读,不允许在聚合初始化(比如std::vector的初始化列表构造)中通过隐式调用explicit构造函数来创建对象,因此触发了错误。
而GCC 6+和Clang对标准的实现进行了调整(或者说修复了这个过于严格的限制),允许在这种嵌套初始化的场景下隐式使用tuple的explicit构造函数,所以能正常编译。
怎么修复?
这里有几种简单的方案,都能让代码兼容GCC 5.5:
1. 显式构造每个tuple
直接用std::tuple{...}或者std::make_tuple显式创建每个tuple元素,跳过隐式转换的步骤:
std::vector< std::tuple<std::vector<std::size_t>, std::vector<std::size_t>, std::vector<std::size_t>> > foo = { std::tuple{{2, 1, 2, 3}, {1, 2}, {2, 3}}, std::make_tuple(std::vector<std::size_t>{2, 3, 4, 0}, std::vector<std::size_t>{3}, std::vector<std::size_t>{2, 3, 4}), std::tuple{{2, 3, 4, 0}, {0}, {3, 4, 0}}, };
2. 分步初始化(更清晰)
先单独创建每个tuple对象,再把它们放进vector里,这种方式可读性更好,也完全避开了编译器的隐式转换问题:
// 先创建每个tuple auto t1 = std::make_tuple( std::vector<std::size_t>{2, 1, 2, 3}, std::vector<std::size_t>{1, 2}, std::vector<std::size_t>{2, 3} ); auto t2 = std::make_tuple( std::vector<std::size_t>{2, 3, 4, 0}, std::vector<std::size_t>{3}, std::vector<std::size_t>{2, 3, 4} ); auto t3 = std::make_tuple( std::vector<std::size_t>{2, 3, 4, 0}, std::vector<std::size_t>{0}, std::vector<std::size_t>{3, 4, 0} ); // 初始化vector std::vector<decltype(t1)> foo = {t1, t2, t3};
3. 升级编译器(如果可行)
如果项目允许升级编译器,直接切换到GCC 6+版本,就能保留你原来的初始化代码,不需要做任何修改——这也是最省心的方案。
内容的提问来源于stack exchange,提问作者Timmmm
相关产品推荐
相关产品推荐

