'Problem with std::index_sequence_for as default argument?

The following C++20 program:

#include <utility>
#include <cstddef>

template<typename... Args>
class C {
    template<size_t... I>
    static void call(
      std::index_sequence<I...> = std::index_sequence_for<Args...>{}
    ) {}
};

int main() {
    C<long int>::call();
}

fails to compile with error message:

test.cc: In static member function ‘static void C<Args>::call(std::index_sequence<I ...>) [with long unsigned int ...I = {}; Args = {long int}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int>]’:
test.cc:11:20: error: could not convert ‘std::index_sequence_for<long int>{}’ from ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’ to ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’
   11 |  C<long int>::call();
      |                    ^
      |                    |
      |                    integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>
test.cc:11:20: note:   when instantiating default argument for call to ‘static void C<Args>::call(std::index_sequence<I ...>) [with long unsigned int ...I = {}; Args = {long int}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int>]’
test.cc: In function ‘int main()’:
test.cc:11:20: error: could not convert ‘std::index_sequence_for<long int>{}’ from ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’ to ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’

Any ideas?

Update:

My current best workaround is to factor out default argument into two functions like:

template<typename... Args>
class C {
    static void call() {
      _call(std::index_sequence_for<Args...>{});
    }

    template<size_t... I>
    static void _call(std::index_sequence<I...>) {}
};

This seems to work around compiler bug (if that's what it is).

Update 2:

The below program fails for the same reason the original one does:

template<typename T> void f(T x = 42) {}

int main() { f(); }

so it's a feature not a bug.



Solution 1:[1]

In general template argument deduction + default function arguments cause a lot of trouble. To simply fix it you can go with this:

#include <utility>
#include <cstddef>

template<typename... Args>
class C {
public:
    static void call() {
        call_impl(std::index_sequence_for<Args...>{});
    }

private:
    template<size_t... I>
    static void call_impl(std::index_sequence<I...> ) {
    }
};

int main() {
    C<long int>::call();
}

Solution 2:[2]

In C++20 you can also write a templated lambda to do exactly that without creating a new function:

//...
static void call() {
    return [&]<size_t... Is>(std::index_sequence<Is...>) {
        /* your code goes here... */ 
    }( std::index_sequence_for<Args...>{} );
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Alex Vask