Search code examples
c++function-parametertemplate-classesctad

Passing designated initializers for a template class to a template function


My goal is to create an API interface that looks like that this:

struct myAB{ int a,b; };

void function(myAB ab) {}

...

function({.a = 1, .b = 3});

The above works just fine. But if I want struct AB to have a templated type, CTAD fails.

template <class B>
struct myAB2{ int a; B b; };
template<typename B> myAB2(int, B) ->  myAB2<B>; 

template<typename B>
void function2(myAB2<B> ab) {}

...

myAB2 ab = {.a = 1, .b = 3}; //works just fine with CTAD
function2(ab); //fine as expected
function2(myAB2{.a = 1, .b = 3}); //works just fine with CTAD
function2({.a = 1, .b = 3}); //fails to compile, can't deduce type 'B'

Why does CTAD fail in the last case? Is there anything I can do to get it to succeed?

https://godbolt.org/z/7Es4Y11Yf


Solution

  • A braced-init-list, including one that contains designated initializers, doesn't have a type. You can only ever deduce std::initializer_list or array types from them, which isn't happening here.

    Since {.a = 1, .b = 3} is not an expression and doesn't have a type, function2({.a = 1, .b = 3}) cannot deduce anything for B from it, so the function can't be called.

    function2<int>({.a = 1, .b = 3}) would work (because the myAB2<int> ab can be initialized with the designated initializers, no more deduction necessary).
    So would function2(myAB2{.a = 1, .b = 3}) (because that does CTAD to get a prvalue of type myAB2<int>, which B = int can be deduced from).