我希望编写一个可变参数工厂函数来转发可变数量的参数(相同类型)来构造和填充一个
std::vector
。但是我不确定如何从“完美转发”机制中提取元素的类型,从而让编译器自动推断出这种类型,并用它来设置向量的类型。
本质上:
template <typename T>
struct Object { T x {}; };
Object<int> x1 {1};
Object<int> x2 {2};
auto collection = make_objects(x1, x2, Object<int>(3));
// 'collection' is a std::vector<Object<int>> with elements that are copies of x1, x2, and the third rvalue parameter.
// note that `make_objects` deduced all types it needed.
我正在使用 C++20 - 完整代码在这里.
让我们从一个基本的具体结构开始:
struct Object {
int x {};
};
我希望使用
std::vector
作为容器创建任意数量的集合。我有一个名为 make_objects
的“工厂”函数,它通过左值和/或右值获取可变数量的 Object 实例,并将它们完美转发给向量的 emplace_back()
成员函数:
template <typename... Args>
auto make_objects(Args&&... args) {
std::vector<Object> vec;
vec.reserve(sizeof...(Args));
(vec.emplace_back(std::forward<Args>(args)), ...);
return vec;
}
这意味着客户端代码几乎不知道集合类型(或者至少可以依赖
auto
),并且可以执行以下操作:
int main() {
Object x1 {1};
Object x2 {2};
auto objects = make_objects(x1, x2, Object(3));
for (auto o: objects) {
std::cout << o << '\n';
}
}
到目前为止一切顺利。
现在我想通过将其转换为模板来使我的
Object
更通用,所以我将其更改为:
template <typename T>
struct Object {
T x {};
};
在这一点上,我对如何编写
make_objects
工厂功能感到困惑。我的第一次尝试是:
template <typename T, typename... Args>
auto make_objects(Args&&... args) {
std::vector<T> vec; // how to specify this type?
vec.reserve(sizeof...(Args));
(vec.emplace_back(std::forward<Args>(args)), ...);
return vec;
}
除非使用明确的
T
调用,否则不会编译,即make_objects<Object<int>>
。否则类型 T 不可推导。
有没有一种方法可以做到这种完美的转发,可以将被转发的同质类型提取出来,用在转发函数中?
请注意,我希望避免以下几种语法:
make_objects<int>(x, ...)
- 我想从参数中推导出对象的类型,但它们至少都是相同的类型(也许 std::same_as
或 std::convertible_to
可以在这里使用来强制执行?)。
make_objects({x, ...})
- 我不想在这种情况下使用初始化列表,因为最终我需要参数是非常量的,因为为了清楚起见,我已经遗漏了预期的副作用。
我一直在研究Homogeneous function parameter packs但这似乎遇到了同样的问题,因为如果
Obj
是一个具体的类,一切都有效,但没有给出(我已经掌握的)关于如何处理模板化Obj<T>
.
make_objects
可以简单地是:
template <typename... Args>
auto make_objects(Args&&... args) {
// use the std::vector deduction guide:
return std::vector{std::forward<Args>(args)...};
}
然后,这将起作用:
int main() {
Object x1 {1};
Object x2 {2};
auto objects1 = make_objects(x1, x2);
//auto objects2 = make_objects(x1, x2, Object{3}); // C++17
auto objects2 = make_objects(x1, x2, Object(3));
for (auto o: objects2) {
std::cout << o << '\n';
}
}
如果你想让
Object{3}
在C++17中工作,添加推导指南:
template<class T> Object(T) -> Object<T>;
如果需要显式命名推导类型,可以这样做:
#include <type_traits>
template <typename Head, typename... Tail>
auto make_objects(Head&& head, Tail&&... tail) {
using T = std::remove_cvref_t<Head>;
static_assert(std::conjunction_v<
std::is_same<T, std::remove_cvref_t<Tail>>...>);
// Do something with T
}