C++模板中Token构造/析构函数重复调用的解决方法咨询
解决C++静态多态中Token构造/析构重复调用的问题
我是C++模板新手,下面的代码是静态多态实现,但存在Token类型的构造函数与析构函数被重复调用的问题,请问如何实现构造函数与析构函数仅调用一次?
#include <iostream> #include <vector> template<typename T> class Scalar { private: T s; public: using value_type = T; Scalar (T const& v): s{v} {std::cout<<"Scalar const\n";} ~Scalar() { std::cout<<"Scalar destruct\n";} T const& operator [] (size_t) const { return s; } size_t size() const { return 0; } }; template<typename T> class Token: private T { public: using value_type = T::value_type; Token (typename T::value_type val): T(val) { std::cout<<"token constructor\n"; } Token (Token const& rhs) = delete; Token (Token&& rhs) =default; ~Token () { std::cout<<"token destruct\n"; } decltype(auto) operator [] (size_t idx) const { std::cout<<"token const ref\n"; return static_cast<T const*>(this)->operator[](idx); } }; template<typename T> std::vector<Token<T>> tokens; template<typename T> void add_scalar (T value) { tokens<Token<Scalar<T>>>.emplace_back(value); std::cout<<"val " << tokens<Token<Scalar<T>>>[0][0]<<'\n'; } int main () { add_scalar<int>(10); }
运行输出:
Scalar const token constructor token constructor token destruct token destruct Scalar destruct
问题原因
核心错误是模板参数嵌套层级错误:
tokens是模板化的std::vector<Token<T>>,当调用tokens<Token<Scalar<T>>>.emplace_back(value)时,vector的实际类型为std::vector<Token<Token<Scalar<int>>>>——你试图在容器中存储Token<Token<Scalar<int>>>类型的对象。emplace_back(value)会先构造一个临时的Token<Scalar<int>>(value),再用这个临时对象构造vector中的Token<Token<Scalar<int>>>元素(依赖默认生成的移动构造函数),这就导致两次Token构造;临时对象随后析构,程序结束时容器内的元素再析构,因此出现两次析构。
解决方案
修正add_scalar函数中tokens的模板参数,直接使用Scalar<T>而非Token<Scalar<T>>,让vector存储Token<Scalar<int>>类型,emplace_back会直接在容器内存中构造对象,避免临时对象的产生:
#include <iostream> #include <vector> template<typename T> class Scalar { private: T s; public: using value_type = T; Scalar (T const& v): s{v} {std::cout<<"Scalar const\n";} ~Scalar() { std::cout<<"Scalar destruct\n";} T const& operator [] (size_t) const { return s; } size_t size() const { return 0; } }; template<typename T> class Token: private T { public: using value_type = T::value_type; Token (typename T::value_type val): T(val) { std::cout<<"token constructor\n"; } Token (Token const& rhs) = delete; Token (Token&& rhs) = default; ~Token () { std::cout<<"token destruct\n"; } decltype(auto) operator [] (size_t idx) const { std::cout<<"token const ref\n"; return static_cast<T const*>(this)->operator[](idx); } }; template<typename T> std::vector<Token<T>> tokens; template<typename T> void add_scalar (T value) { // 修正模板参数:用Scalar<T>代替Token<Scalar<T>> tokens<Scalar<T>>.emplace_back(value); std::cout<<"val " << tokens<Scalar<T>>[0][0]<<'\n'; } int main () { add_scalar<int>(10); }
运行输出:
Scalar const token constructor token const ref val 10 token destruct Scalar destruct
额外说明
emplace_back的作用是直接在容器内存中构造对象,避免拷贝/移动临时对象,但前提是模板参数要匹配,否则会先构造临时对象再转换,反而带来额外的构造析构。- 你删除
Token的拷贝构造函数、保留默认移动构造函数的做法是正确的,但模板参数错误时,依然会触发移动构造和临时对象的析构。
内容的提问来源于stack exchange,提问作者Дима Зверинцев
相关产品推荐
相关产品推荐

