How to get compiler to deduce parameters for function with `concept auto…` given initializer lists

I am trying to get the hang of C++20 concepts. I want to define a function taking an arbitrary number of Foo instances and have the compiler implicitly convert input arguments to Foo instances. The problem is that the implicit conversion does not seem to work if I use initializer_lists. The last line in the following code is the offending one.

Is this possible with concepts and C++20? If yes, how should I change my code? If no, is there any other way to let the compiler to automatically do the implicit conversion?

#include <iostream>
#include <concepts>

struct Foo {
    Foo(){ std::cout << "No argument!\n"; }
    Foo(int){ std::cout << "One argument!\n"; }
    Foo(int, int){ std::cout << "Two arguments!\n"; }

// function takes a single Foo
void foo(Foo foo){}

// function takes any number of Foos
template <typename... Foos>
requires (std::is_convertible_v<Foos, Foo> && ...)
void foos(Foos... foos){}
int main()
    // converting ctors work
    Foo x = 42;
    Foo y = {42, 42};
    Foo z = {};

    std::cout << "\n";

    // argument deduction works for foo
    foo({42, 42});

    std::cout << "\n";

    // argument deduction works for foos using only second ctor
    foos(1, 2, 3);

    std::cout << "\n";

    // argument deduction does not work for foos if we use initializer lists
    foos(42, {42, 42}, {});

GCC complains:

 error: too many arguments to function 'void foos(Foos ...) [with Foos = {}]'

MSVC complains:

'initializer list': is not a valid template argument for 'Foos'

Clang complains:

candidate template ignored: substitution failure: deduced incomplete pack <int, (no value), (no value)> for template parameter 'Foos'

Godbolt link

>Solution :

{..} has no types, and can only be deduced as std::ininitilizer_list<T> or T[N].

As alternative, you might use

void foos(std::initializer_list<Foo> foos){/**/}

with call similar to:

foos({42, {42, 42}, {}});

Leave a Reply