从 make_integer_sequence 到 integer_sequence 的转换错误

conversion error from make_integer_sequence to integer_sequence

以下程序无法编译:

#include <utility>
#include <iostream>

#define N 4

template <unsigned int I>
unsigned int g() { return I; }

template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...> = std::make_integer_sequence<unsigned int, N>{})
{
  return (g<I>() + ...);
}

int main()
{
  std::cout << f() << std::endl;
  return 0;
}

测试一下live on Coliru

使用 gcc 的错误是

main.cpp: In function 'unsigned int f(std::integer_sequence<unsigned int, I ...>) [with unsigned int ...I = {}]':

main.cpp:17:18: error: could not convert 'std::make_integer_sequence<unsigned int, 4>{}' from 'integer_sequence<[...],'nontype_argument_pack' not supported by dump_expr>' to 'integer_sequence<[...],'nontype_argument_pack' not supported by dump_expr>'

用clang++报类似的转换错误:

error: no viable conversion from 'std::make_integer_sequence<unsigned int, 4>' (aka '__make_integer_seq<integer_sequence, unsigned int, 4U>') to 'std::integer_sequence'

然而,奇怪的是,如果我删除默认参数,并将相同的表达式传递给 f,程序编译并给出更正的输出:

#include <utility>
#include <iostream>

#define N 4

template <unsigned int I>
unsigned int g() { return I; }

template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...>)
{
  return (g<I>() + ...);
}

int main()
{
  std::cout << f(std::make_integer_sequence<unsigned int, N>{}) << std::endl;
  return 0;
}

看到了live on Coliru.

第一个代码的 problem/difference 是什么?

我承认,我不明白错误信息。第二个版本编译但不是第一个版本的原因是模板参数不能从默认参数推导出来,而是从函数参数推导出来。考虑这个更简单的例子:

#include <utility>
#include <iostream>

template <unsigned int>
struct foo {};

template <unsigned int x>
foo<x> make_foo(){ return {};}

template <unsigned int x>
unsigned int f(foo<x> = make_foo<4>())
{
  return 42;
}

int main()
{
  std::cout << f() << std::endl;
  return 0;
}

这里的错误更具描述性:

<source>: In function 'int main()':
<source>:18:18: error: no matching function for call to 'f()'
   18 |   std::cout << f() << std::endl;
      |                  ^
<source>:11:14: note: candidate: 'template<unsigned int x> unsigned int f(foo<x>)'
   11 | unsigned int f(foo<x> = make_foo<4>())
      |              ^
<source>:11:14: note:   template argument deduction/substitution failed:
<source>:18:18: note:   couldn't deduce template parameter 'x'
   18 |   std::cout << f() << std::endl;
      |                  ^

make_integer_sequence<unsigned int,N> 的主要目的是从单个 N 过渡到 std::integer_sequence<unsigned int, I...> 中的包,就像您在第二个示例中所做的那样。

通过间接级别,您可以避免调用者必须传递参数:

// ...

template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...>)
{
  return (g<I>() + ...);
}

template <unsigned int X = N>
unsigned int f_wrap()
{
  return f(std::make_integer_sequence<unsigned int,N>{});
}

int main()
{
  std::cout << f_wrap() << std::endl;
  return 0;
}

Live Demo