【问题标题】:Variadic template packed argument to std::vectorstd::vector 的可变参数模板打包参数
【发布时间】:2020-04-28 18:25:51
【问题描述】:

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

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的类型是什么?
  • 那很痛????,我知道问题应该出在 std::vector 但我不知道如何使它仅适用于整数。

标签: c++ templates variadic-templates


【解决方案1】:

您的代码中的问题是 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 看起来很有趣,但我如何实现它?
【解决方案2】:

另一种略显冗长的方法是添加一个初始模板参数,指定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,这可能会给出更清晰的错误消息。

【讨论】:

    【解决方案3】:

    当您执行std::vector&lt;T&gt; 时,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以外的任何东西来调用这个函数,但它可以有你想要的任意多个。

    【讨论】:

    • 谢谢,它有效,但我不明白为什么在折叠版本中你放了一个昏迷 endl), ..." 。请你给我解释一下好吗?
    • @ElGusanito 我正在做的是滥用逗号运算符。我的意思是为参数包的每个参数做(std::cout &lt;&lt; args &lt;&lt; std::endl)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    • 2016-06-25
    相关资源
    最近更新 更多