如何从具有同质模板类型参数的完美转发函数中提取模板类型?

问题描述 投票:0回答:2

我希望编写一个可变参数工厂函数来转发可变数量的参数(相同类型)来构造和填充一个

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>
.

c++ templates variadic-templates stdvector perfect-forwarding
2个回答
1
投票

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>;

1
投票

如果需要显式命名推导类型,可以这样做:

#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
}
© www.soinside.com 2019 - 2024. All rights reserved.