C++中Box类同时支持初始化列表与vector构造的编译问题
问题原因分析
问题场景
希望Box类对象支持两种初始化方式:
- 通过初始化列表直接初始化,如
Box b{1,2,3} - 通过传入
std::vector对象初始化,如std::vector<int> v{1,2,3}; Box b{v}
最初使用接收std::vector<int>和非模板initializer_list<int>的构造函数时运行正常,但因为实际场景中Box的成员是自定义Obj类的vector(Obj可由int或double构造),将初始化列表构造函数改为模板版本后,使用vector构造Box时出现编译错误:编译器尝试调用模板构造函数,导致m_obj.push_back(e)无匹配成员函数。
复现代码
#include <iostream> #include <vector> #include <initializer_list> class Obj{ public: Obj(int) {std::cout << "int constructor\n";} Obj(double) {std::cout << "double constructor\n";} ~Obj() {} }; class Box{ public: std::vector<Obj> m_obj; explicit Box(const std::vector<Obj> v) : m_obj{v} {} template <typename T> explicit Box(const std::initializer_list<T>& ls) { for(const T& e: ls) m_obj.push_back(e); } ~Box() {} }; int main() { std::vector<int> v{1, 2}; Box b1{ 1, 2 }; Box b2{ 1.1, 2.2 }; // Box b3{v}; // 触发上述错误 }
原因解析
当尝试用Box b3{v}初始化时,编译器会进行重载决议:
- 对于非模板构造函数
Box(const std::vector<Obj> v),需要将std::vector<int>隐式转换为std::vector<Obj>,这个转换需要逐个把int元素构造为Obj,属于用户定义的转换,转换层级更高。 - 对于模板构造函数
template <typename T> Box(const std::initializer_list<T>& ls),编译器会触发列表初始化的特殊规则:当用{v}这种形式初始化时,会尝试把单个元素v包裹成临时的initializer_list<std::vector<int>>,此时模板参数T被推导为std::vector<int>,这个匹配过程不需要任何用户定义的转换,直接就能推导成功。
因为模板构造函数的匹配优先级更高,编译器会选择它而非我们期望的vector重载构造函数。但在模板构造函数内部,尝试把std::vector<int>类型的元素e插入到std::vector<Obj>中时,Obj并没有接收std::vector<int>的构造函数,因此出现push_back无匹配成员函数的编译错误。
简单来说,列表初始化的规则让编译器优先匹配了模板版的initializer_list构造函数,而非目标的vector构造函数,进而引发类型不兼容的错误。
内容的提问来源于stack exchange,提问作者phantum12265
相关产品推荐
相关产品推荐

