如何创建检查return语句合法性的C++ concept并实现可透明返回的Box包装类
现有实现的问题
你编写的模板构造函数没有排除参数为Box<T>本身的场景:当尝试拷贝/移动Box<T>实例时,泛型构造函数的转发引用优先级高于编译器默认生成的拷贝/移动构造,会被优先匹配,此时会尝试用Box<T>类型去构造内部的T成员,自然编译失败。
最优实现方案(C++17及以上)
如果你使用C++17及以上版本,零自定义构造函数的聚合类方案完全满足需求,也是构造函数最少的实现:
template <typename T> struct Box { T t; };
该方案的原理:
- 没有自定义构造函数,属于聚合类,符合C++17的拷贝列表初始化规则,所有能用来初始化
T的返回表达式,都可以直接用来初始化Box<T> - 编译器自动生成的拷贝、移动、析构函数全部符合预期,无需额外编码
- 支持所有返回场景:
// 单值返回 Box<std::string> get_str() { return "hello world"; } // 编译通过 // 初始化列表返回 Box<std::pair<int, int>> get_pair() { return {1, 2}; } // 编译通过 // 拷贝返回 Box<int> b{42}; Box<int> get_box() { return b; } // 编译通过
兼容低版本C的实现(C11及以上)
如果需要兼容C++11/14,或者需要添加自定义封装逻辑必须编写构造函数,可以修正原实现,显式声明拷贝/移动构造避免泛型构造抢匹配,同时在约束中排除Box<T>自身类型的参数:
template <typename T> class Box { public: Box(const Box&) = default; Box(Box&&) = default; template <typename... Ts> constexpr Box(Ts&&... ts) noexcept(std::is_nothrow_constructible_v<T, Ts...>) : t(std::forward<Ts>(ts)...) { static_assert( sizeof...(Ts) != 1 || !std::is_same<Box, typename std::decay<Ts...>::type>::value, "Invalid parameter type" ); static_assert( std::is_constructible<T, Ts...>::value, "T cannot be constructed from given parameters" ); } private: T t; };
如果使用C++20,可以把static_assert替换为concept约束,写法更简洁:
template <typename T> class Box { public: Box(const Box&) = default; Box(Box&&) = default; template <typename... Ts> requires ( (sizeof...(Ts) != 1 || !std::same_as<Box, std::decay_t<Ts>>) && std::constructible_from<T, Ts...> ) constexpr Box(Ts&&... ts) noexcept(std::is_nothrow_constructible_v<T, Ts...>) : t(std::forward<Ts>(ts)...) {} private: T t; };
内容的提问来源于stack exchange,提问作者vojana
相关产品推荐
相关产品推荐

