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::variant
与 std::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。
我是模板的新手,我真的不明白为什么这不起作用。我希望用这些值构造向量。
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::variant
与 std::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。