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

3

我对模板还不熟悉,不太明白为什么这个不起作用。我期望向量会使用这些值进行构建。

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

int_printf(3, nullptr, -9e134, "hello", main); 的类型是什么? vec 的类型是什么? - Mooing Duck
那很痛苦,我知道问题应该在std::vector<T>中,但我不知道如何让它只适用于整数。 - cdecompilador
4个回答

5
您代码中的问题在于,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 的参数具有公共类型时才能起作用。


@MooingDuck 看起来很有趣,但我该如何实现它? - cdecompilador

3

另一种略微啰嗦的方法是通过添加一个初始模板参数来指定 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,这可能会提供更清晰的错误消息。


2
当你使用 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;
    }
}

您也可以使用 折叠表达式,完全跳过向量。
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), ...);
}

现在你只能用整数调用这个函数,但可以有任意多个整数作为参数。

谢谢,它有效,但我不明白为什么在折叠版本中你要放一个逗号 endl), ..."。你能解释一下吗? - cdecompilador
1
@ElGusanito 我所做的是滥用逗号运算符。我的意思是对参数包中的每个参数执行(std::cout << args << std::endl) - NathanOliver

0

您可以将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。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接