std::vector 的可变参数模板打包参数

Variadic template packed argument to std::vector

我是模板的新手,我真的不明白为什么这不起作用。我希望用这些值构造向量。

main.cpp


template <typename ...T>
void int_printf(T ...args)
{
    std::vector<T> vec = {args...};

    for(auto& v:vec)
    {
        std::cout << v << std::endl;
    }
}

int main()
{
    int_printf(1,2,3,4);

    return 0;
}

预期结果

1
2
3
4

msvc 编译器出错(已翻译)

src/main.cpp(35): error C3520: 'T': the parameter pack must be expanded in this context
src/main.cpp(37): error C3536: '<begin>$L0': can't be used before initialization
src/main.cpp(37): error C3536: '<end>$L0': can't be used before initialization
src/main.cpp(37): error C2100: invalid redirection

您的代码中的问题是 T 在此上下文中不是模板参数,它是模板参数包,在您的示例中将扩展为 T=[int,int,int,int]std::vector 期望类型作为模板参数传递,而不是模板参数包。您可以使用 std::common_type:

解决此问题
#include<type_traits>

template <typename ...T>
void int_printf(T ...args)
{
    //use std::common_type to deduce common type from template
    //   parameter pack
    std::vector<typename std::common_type<T...>::type> vec = {args...};

    for(auto& v:vec)
    {
        std::cout << v << std::endl;
    }
}

您应该注意,这仅在传递给 int_printf 的参数具有共同类型时才有效。

当您执行 std::vector<T> 时,T 不是单一类型,而是一组类型。您不能将它用于向量,因为它需要单一类型的元素。

有几种方法可以解决这个问题。首先是硬编码向量的类型。这使得代码不那么通用,但对你有用,因为你的函数被称为 int_printf 而不是 anything_print

另一种选择是使用 std::common_type 来获取元素的常见类型,例如

template <typename ...T>
void int_printf(T ...args)
{
    std::vector<std::common_type_t<T...>> vec = {args...};

    for(auto& v:vec)
    {
        std::cout << v << std::endl;
    }
}

您也可以使用 fold expression 并像

一样完全跳过矢量
template <typename ...T>
void int_printf(T ...args)
{
    ((std::cout << args << std::endl), ...);
//  ^^                                    ^
//  |          do this part         ^  ^  |
//  |              for each parameter  |  |
//  start fold expression          end fold
}

如果您只想要无限数量的 int,您还可以使用 SFINAE 将包类型限制为整数,例如

template <typename ...T, std::enable_if_t<std::conjunction_v<std::is_same<T, int>...>, bool> = true>
void int_printf(T ...args)
{
    ((std::cout << args << std::endl), ...);
}

现在你不能用 int 以外的任何东西调用这个函数,但它可以有你想要的任意多个。

另一种稍微冗长的方法是添加一个初始模板参数来指定 vec 的类型,如下所示:

#include <iostream>
#include <vector>

template <typename T, typename ... Args>
void int_printf(Args ... args)
{
    std::vector<T> vec = {args...};

    for (auto& v : vec)
    {
        std::cout << v << std::endl;
    }
}

int main()
{
    int_printf<int>(1,2,3,4);
    return 0;
}

如果您将不兼容的类型列表传递给 int_printf,这可能会给出更清晰的错误消息。

您可以将 std::variantstd::vector 结合使用。这是一个例子:

template<typename... Args>
class VariantTest
{
private:
    using ArgTypes = std::variant<Args...>;
    std::vector<ArgTypes> elems;
public:
    VariantTest()
    {
        elems.reserve(10); //just a number
    }

    template<typename... ArgsL>
    void AddTypes(ArgsL&&... args)
    {
        (elems.emplace_back(std::forward<ArgsL>(args)), ...);
    }
    size_t GetElemsCount()
    {
        return elems.size();
    }
};

int main()
{

    VariantTest<A, B> vt;
    vt.AddTypes(B(), A()); //Note the order does not matter.
    std::cout << "Number of elements: " << vt.GetElemsCount() << '\n';
    return 0;
}

您将需要 C++17。