总结一个 std::integer_sequence 中的元素
Summing up the elements in a std::integer_sequence
出于教育目的,我尝试创建一个 std::integer_sequence
并将其元素汇总为参数包。我希望这很简单,并在下面编写了代码。
第 1 步:创建了一组 add() 操作以正确处理同类的、基于整数的参数包。使用 add<0,1,2,3>()
-call 进行测试并有效。
第 2 步:在这一步中,我想传递一个 std::integer_sequence
作为模板参数,以对序列中的整数求和。我失败了,看注释掉的代码。
如何将 std::integer_sequence
作为模板参数传递?
一个错误消息是函数模板总数的实例没有与调用匹配。为什么?我希望整数序列等同于 int
参数包。
另一条错误消息说 Args
模板参数是非常量。怎么可能呢?我希望这是一个后续错误,但我在实验中经常看到这种错误组合。
我正在使用 Microsoft VC++ 2017 编译器。
#include <iostream>
#include <utility>
template <typename T>
T add(T first) {
return first;
}
template <typename T, typename ... Args>
T add(T car, Args... cdr) {
return car + add(cdr...);
}
template <int ... Args>
int total() {
return add(Args...);
}
int main(int argc, char** argv)
{
using std::cout;
using std::endl;
int s1 =
total<0, 1, 2, 3>();
std::cout << "s1 = " << s1 << std::endl;
// The following does not compile:
#if 0
int s2 =
total<std::make_integer_sequence<int, 4>>();
std::cout << "s2 = " << s2 << std::endl;
static_assert(s1 == s2, "no match");
#endif
// This should work:
//
{
cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
cout << std::make_integer_sequence<int, 4>().size << endl;;
}
return 0;
}
... 后来经过了很多有帮助的评论。我学到了很多东西,现在有了以下工作代码:
#include <iostream>
#include <utility>
template <typename T>
constexpr T add(T first) {
return first;
}
template <typename T, typename ... Args>
constexpr T add(T car, Args... cdr) {
return car + add(cdr...);
}
template <int ... Args>
constexpr int total() {
return add(Args...);
}
template<int... Args>
constexpr int total(std::integer_sequence<int, Args...>) {
return total<Args...>();
}
int main(int argc, char** argv)
{
using std::cout;
using std::endl;
constexpr int s1 =
total<0, 1, 2, 3>();
std::cout << "s1 = " << s1 << std::endl;
// The following now compiles. Oh, happy day!
#if 1
constexpr int s2 =
total(std::make_integer_sequence<int, 4> {});
std::cout << "s2 = " << s2 << std::endl;
static_assert(s1 == s2, "no match");
#endif
// This should work:
//
{
cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
cout << std::make_integer_sequence<int, 4>().size << endl;;
}
return 0;
}
这样做的一种方法是为 total
添加一个重载并以不同的方式调用它:
template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
return add(Args...);
}
而不是调用
nt s2 =
total(std::make_integer_sequence<int, 4>>{});
这将解决通话问题。为了在 static_assert
中使用函数的结果,您还需要确保它(以及存储它的结果的变量)被标记为 constexpr
.
使用 fold expressions 会容易得多:
template<class T, T... Args>
constexpr T total(std::integer_sequence<T, Args...> = {}) {
return (Args + ...);
}
int main() {
int s2 =
total(std::make_integer_sequence<int, 4>{});
// Or calling directly
int s1 = total<int, 0, 1, 2, 3>();
}
请注意默认的 std::integer_sequence
参数,因此如果您传递一个整数序列,它将推导出 Args
参数。
如果您希望能够在模板中使用 std::integer_sequence
类型调用它,只需创建一个辅助函数:
template<class IntegerSequence>
constexpr auto total_of(IntegerSequence argument = {}) {
return total(argument); // Will deduce `T` and `Args`
}
total_of<std::make_integer_sequence<int, 4>>();
I expected an integer sequence to be equivalent to an int parameter pack.
但是你可以通过写出以下的两个扩展来看出它不是:
template <int ... Args> int total();
第一种情况是
total<0, 1, 2, 3>();
// => Args... is (0, 1, 2, 3)
这是一个有效的整数包,第二个是
total<std::make_integer_sequence<int, 4>>();
// => Args... must be std::integer_sequence<int, 0, 1, 2, 3>
这显然与整数包完全不同。 Link 到 std::integer_sequence
的文档以供参考。
如果您想将该整数序列转回传递给 make_integer_sequence
的原始参数包,您需要使用模板参数推导重载 SergeyA 的答案显示:
template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
return add(Args...);
}
(函数参数是一个虚拟参数,只是为了允许推导 Args...
)。
不过,如果可以的话,折叠表达式比旧式递归要好得多。
I expected an integer sequence to be equivalent to an int parameter pack.
不是:std::integer_sequence
是一种类型;所以
total<std::make_integer_sequence<int, 4>>();
可以匹配到接收类型
的模板total()
函数
template <typename T>
int total ()
{ return ???; }
问题是您无法从类型 T
中轻松提取整数序列。
How can I pass a std::integer_sequence as a template parameter?
解决方案(好吧……一个可能的解决方案)是偏特化。
遗憾的是,函数不能部分特化。
但如果 total
是 struct
(或 class
),则一切都不一样了。
可以这样写total
template <typename>
struct total;
template <typename T, T ... Is>
struct total<std::integer_sequence<T, Is...>>
: public std::integral_constant<T, (Is + ...)>
{ };
所以你可以通过以下方式使用它
constexpr auto s2 { total<std::make_integer_sequence<int, 4>>::value };
题外话:声明constexpr
你想在static_assert()
测试中使用的变量。
以下是完整的 C++17 编译示例(total
结构重命名为 total2
以避免与 total()
函数冲突)
#include <utility>
template <typename>
struct total2;
template <typename T, T ... Is>
struct total2<std::integer_sequence<T, Is...>>
: public std::integral_constant<T, (Is + ...)>
{ };
template <typename T>
constexpr T add (T first)
{ return first;}
template <typename T, typename ... Args>
constexpr T add (T car, Args ... cdr)
{ return car + add(cdr...); }
template <int ... Args>
constexpr int total ()
{ return add(Args...); }
int main ()
{
constexpr auto s1 { total<0, 1, 2, 3>() };
constexpr auto s2 { total2<std::make_integer_sequence<int, 4>>::value };
static_assert(s1 == s2);
}
出于教育目的,我尝试创建一个 std::integer_sequence
并将其元素汇总为参数包。我希望这很简单,并在下面编写了代码。
第 1 步:创建了一组 add() 操作以正确处理同类的、基于整数的参数包。使用 add<0,1,2,3>()
-call 进行测试并有效。
第 2 步:在这一步中,我想传递一个 std::integer_sequence
作为模板参数,以对序列中的整数求和。我失败了,看注释掉的代码。
如何将 std::integer_sequence
作为模板参数传递?
一个错误消息是函数模板总数的实例没有与调用匹配。为什么?我希望整数序列等同于 int
参数包。
另一条错误消息说 Args
模板参数是非常量。怎么可能呢?我希望这是一个后续错误,但我在实验中经常看到这种错误组合。
我正在使用 Microsoft VC++ 2017 编译器。
#include <iostream>
#include <utility>
template <typename T>
T add(T first) {
return first;
}
template <typename T, typename ... Args>
T add(T car, Args... cdr) {
return car + add(cdr...);
}
template <int ... Args>
int total() {
return add(Args...);
}
int main(int argc, char** argv)
{
using std::cout;
using std::endl;
int s1 =
total<0, 1, 2, 3>();
std::cout << "s1 = " << s1 << std::endl;
// The following does not compile:
#if 0
int s2 =
total<std::make_integer_sequence<int, 4>>();
std::cout << "s2 = " << s2 << std::endl;
static_assert(s1 == s2, "no match");
#endif
// This should work:
//
{
cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
cout << std::make_integer_sequence<int, 4>().size << endl;;
}
return 0;
}
... 后来经过了很多有帮助的评论。我学到了很多东西,现在有了以下工作代码:
#include <iostream>
#include <utility>
template <typename T>
constexpr T add(T first) {
return first;
}
template <typename T, typename ... Args>
constexpr T add(T car, Args... cdr) {
return car + add(cdr...);
}
template <int ... Args>
constexpr int total() {
return add(Args...);
}
template<int... Args>
constexpr int total(std::integer_sequence<int, Args...>) {
return total<Args...>();
}
int main(int argc, char** argv)
{
using std::cout;
using std::endl;
constexpr int s1 =
total<0, 1, 2, 3>();
std::cout << "s1 = " << s1 << std::endl;
// The following now compiles. Oh, happy day!
#if 1
constexpr int s2 =
total(std::make_integer_sequence<int, 4> {});
std::cout << "s2 = " << s2 << std::endl;
static_assert(s1 == s2, "no match");
#endif
// This should work:
//
{
cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
cout << std::make_integer_sequence<int, 4>().size << endl;;
}
return 0;
}
这样做的一种方法是为 total
添加一个重载并以不同的方式调用它:
template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
return add(Args...);
}
而不是调用
nt s2 =
total(std::make_integer_sequence<int, 4>>{});
这将解决通话问题。为了在 static_assert
中使用函数的结果,您还需要确保它(以及存储它的结果的变量)被标记为 constexpr
.
使用 fold expressions 会容易得多:
template<class T, T... Args>
constexpr T total(std::integer_sequence<T, Args...> = {}) {
return (Args + ...);
}
int main() {
int s2 =
total(std::make_integer_sequence<int, 4>{});
// Or calling directly
int s1 = total<int, 0, 1, 2, 3>();
}
请注意默认的 std::integer_sequence
参数,因此如果您传递一个整数序列,它将推导出 Args
参数。
如果您希望能够在模板中使用 std::integer_sequence
类型调用它,只需创建一个辅助函数:
template<class IntegerSequence>
constexpr auto total_of(IntegerSequence argument = {}) {
return total(argument); // Will deduce `T` and `Args`
}
total_of<std::make_integer_sequence<int, 4>>();
I expected an integer sequence to be equivalent to an int parameter pack.
但是你可以通过写出以下的两个扩展来看出它不是:
template <int ... Args> int total();
第一种情况是
total<0, 1, 2, 3>();
// => Args... is (0, 1, 2, 3)
这是一个有效的整数包,第二个是
total<std::make_integer_sequence<int, 4>>();
// => Args... must be std::integer_sequence<int, 0, 1, 2, 3>
这显然与整数包完全不同。 Link 到 std::integer_sequence
的文档以供参考。
如果您想将该整数序列转回传递给 make_integer_sequence
的原始参数包,您需要使用模板参数推导重载 SergeyA 的答案显示:
template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
return add(Args...);
}
(函数参数是一个虚拟参数,只是为了允许推导 Args...
)。
不过,如果可以的话,折叠表达式比旧式递归要好得多。
I expected an integer sequence to be equivalent to an int parameter pack.
不是:std::integer_sequence
是一种类型;所以
total<std::make_integer_sequence<int, 4>>();
可以匹配到接收类型
的模板total()
函数
template <typename T>
int total ()
{ return ???; }
问题是您无法从类型 T
中轻松提取整数序列。
How can I pass a std::integer_sequence as a template parameter?
解决方案(好吧……一个可能的解决方案)是偏特化。
遗憾的是,函数不能部分特化。
但如果 total
是 struct
(或 class
),则一切都不一样了。
可以这样写total
template <typename>
struct total;
template <typename T, T ... Is>
struct total<std::integer_sequence<T, Is...>>
: public std::integral_constant<T, (Is + ...)>
{ };
所以你可以通过以下方式使用它
constexpr auto s2 { total<std::make_integer_sequence<int, 4>>::value };
题外话:声明constexpr
你想在static_assert()
测试中使用的变量。
以下是完整的 C++17 编译示例(total
结构重命名为 total2
以避免与 total()
函数冲突)
#include <utility>
template <typename>
struct total2;
template <typename T, T ... Is>
struct total2<std::integer_sequence<T, Is...>>
: public std::integral_constant<T, (Is + ...)>
{ };
template <typename T>
constexpr T add (T first)
{ return first;}
template <typename T, typename ... Args>
constexpr T add (T car, Args ... cdr)
{ return car + add(cdr...); }
template <int ... Args>
constexpr int total ()
{ return add(Args...); }
int main ()
{
constexpr auto s1 { total<0, 1, 2, 3>() };
constexpr auto s2 { total2<std::make_integer_sequence<int, 4>>::value };
static_assert(s1 == s2);
}