为何类模板推导时,某参数列表可构造std::vector却无法构造std::array?
在不指定任何模板参数初始化std::array对象时,遇到了不对称现象:代码中的v3和a4可以正常编译,但a3却编译失败,请解释原因。
相关代码及编译错误信息如下:
#include <vector> #include <array> int main() { auto v1 = std::vector<std::pair<int,int>>{ {1,2}, {3,4} }; auto v2 = std::vector{ std::pair{1,2}, std::pair{3,4} }; auto v3 = std::vector{ std::pair{1,2}, {3,4} }; auto v4 = std::vector{ std::initializer_list{ std::pair{1,2}, {3,4} } }; auto a1 = std::array<std::pair<int,int>, 2>{ {{1,2}, {3,4}} }; auto a2 = std::array{ std::pair{1,2}, std::pair{3,4} }; auto a3 = std::array{ std::pair{1,2}, {3,4} }; // <= 编译失败 auto a4 = std::array{ std::initializer_list{ std::pair{1,2}, {3,4} } }; return 0; }
编译错误:
<source>: In function 'int main()': <source>:13:49: error: class template argument deduction failed: 13 | auto a3 = std::array{ std::pair{1,2}, {3,4} }; // <= 编译失败 | ^ <source>:13:49: error: no matching function for call to 'array(std::pair<int, int>, <brace-enclosed initializer list>)'
编辑说明:本问题与其他涉及指定类型std::array的问题并不相似,因为这里讨论的是不指定模板参数的std::array。注:此观点并非绝对。
1. v3编译通过的原因
std::vector的类模板参数推导(CTAD)逻辑更灵活:当传入混合了显式类型对象和braced-init-list的初始化器时,编译器会先从第一个明确类型的元素(std::pair{1,2})推导出vector的元素类型为std::pair<int,int>,然后自动将第二个braced-init-list{3,4}隐式转换为std::pair<int,int>,匹配vector接受初始化列表的构造函数,因此编译正常。
2. a3编译失败的原因
std::array是固定大小的容器,它的CTAD规则严格得多:推导模板参数T(元素类型)和N(大小)时,要求所有初始化元素的类型必须一致,且能被明确推导为同一类型。
在a3中,第一个元素是明确的std::pair<int,int>,第二个是无类型的braced-init-list{3,4}——编译器无法将这两个元素视为同类型来完成std::array的模板参数推导,因为std::array的CTAD不支持混合显式类型对象和未推导的braced-init-list的场景,找不到匹配的推导规则,因此报错。
3. a4编译通过的原因
a4中显式构造了std::initializer_list{ std::pair{1,2}, {3,4} },这个初始化列表会被先推导为std::initializer_list<std::pair<int,int>>。而std::array的CTAD有专门处理std::initializer_list<T>的规则:用T作为数组的元素类型,用初始化列表的元素数量作为数组的大小N。这里初始化列表有2个元素,因此推导出std::array<std::pair<int,int>, 2>,后续初始化逻辑完全匹配,编译通过。
内容的提问来源于stack exchange,提问作者MarkB

