为何在该场景下需显式指定默认构造对象的类型?
问题:为何
foobar返回tuple时第二个元素必须显式指定std::vector<int>{},而foobar2可直接用{}? 代码示例
#include <iostream> #include <memory> #include <vector> #include <tuple> std::tuple<std::unique_ptr<int>, std::vector<int>> foobar() { std::unique_ptr<int> test = std::make_unique<int>(42); return { std::move(test), {} }; // <= 语法错误 // return { std::move(test), std::vector<int>{} } // <= 编译通过 } std::tuple<int, std::vector<int>> foobar2() { return { {}, {} }; // 编译正常 } int main() { std::cout << *std::get<0>(foobar()) << "\n"; std::cout << std::get<0>(foobar2()) << "\n"; return 0; }
GCC编译错误信息
<source>: In function 'std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::vector<int, std::allocator<int> > > foobar()': <source>:8:34: error: could not convert '{std::move<unique_ptr<int>&>(test), <brace-enclosed initializer list>()}' from '<brace-enclosed initializer list>' to 'std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::vector<int, std::allocator<int> > >' 8 | return { std::move(test), {} }; // <= 语法错误 | ^ | | | <brace-enclosed initializer list> Compiler returned: 1
原因解析
核心问题在于C++列表初始化的类型推导规则,以及std::tuple构造函数的匹配逻辑:
foobar2能正常编译的原因foobar2返回的tuple元素类型是int和std::vector<int>:- 第一个
{}会被隐式初始化为int类型的0; - 第二个
{}会被隐式初始化为空的std::vector<int>;
编译器可以明确将这两个空列表匹配到tuple对应元素的类型,顺利完成构造。
- 第一个
foobar编译失败的原因foobar返回的tuple第一个元素是std::unique_ptr<int>,这是一个具有特殊构造规则的智能指针类型:- 当你写
{ std::move(test), {} }时,第二个元素是未指定具体类型的空初始化列表(braced-init-list); - 由于第一个元素是
std::unique_ptr,编译器在匹配std::tuple的构造函数时,无法推导这个空列表应该转换为std::vector<int>——空初始化列表本身没有类型,而std::unique_ptr的存在让编译器无法确定唯一的构造函数重载候选; - 必须显式写出
std::vector<int>{},给编译器明确的类型提示,才能让它正确匹配tuple的构造,完成两个元素的初始化。
- 当你写
替代写法
除了显式指定std::vector<int>{},也可以用std::make_tuple来避免列表初始化的推导问题:
return std::make_tuple(std::move(test), std::vector<int>{});
内容的提问来源于stack exchange,提问作者jwezorek
相关产品推荐
相关产品推荐

