1

The following program does not compile:

#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;
}

Test it live on Coliru.

With gcc the error is

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>'

A similar conversion error is reported with 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'

Strangely enough, however, if I remove the default parameter, and pass the same expression to f, the program compiles and gives the corrected output:

#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;
}

See it live on Coliru.

What's the problem/difference with the first code?

1 Answer 1

1

I admit, I do not understand the error message. The reason the second version compiles but not the first is that template parameters cannot be deduced from default arguments but from function parameters. Consider this simpler example:

#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;
}

Here the error is a little more descriptive:

<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;
      |                  ^

The main purpose of make_integer_sequence<unsigned int,N> is to make the transition from a single N to the pack in std::integer_sequence<unsigned int, I...> as you do it in your second example.

With a level of indirection you avoid that the caller must pass the parameter:

// ...

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.