1

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

Artyer
  • 31,034
  • 3
  • 47
  • 75
dada_dave
  • 493
  • 4
  • 13
  • The code in your question does not match the code in the godbolt link. – Paul Sanders Jul 10 '23 at 20:11
  • Looks like you can't deduce a template argument from an initializer list (which seems reasonable). `function2(myAB2{.a =1, .b =3});` is probably the best you can do. – Paul Sanders Jul 10 '23 at 20:17
  • ah sorry, fixed the major discrepancies I think (unlike my original thinking above I put both examples into one Godbolt file and I wanted to keep things as simple as possible, so I renamed everything to 2) – dada_dave Jul 10 '23 at 20:21
  • function2(myAB2{.a = 1, .b = 3}); works, but I still find it odd that passing the structured binding, essentially an initializer list, with no structure name to a variable works but passing it to a function parameter of the same type does not – dada_dave Jul 10 '23 at 20:23
  • "Is there anything I can do to get it to succeed?" Write and submit a brilliant proposal for adding named function arguments to the next C++ standard. No guarantees though. – n. m. could be an AI Jul 10 '23 at 20:35
  • :) while that would be ideal, I'm afraid I lack the compiler hacking skills to get a proposal prototyped and I'm very sure that such proposals have common and gone from those smarter and more experienced than myself. Passing structured bindings is just so very close except for the CTAD failure and that just seems like such a pity. I'm still not sure why the CTAD fails in this case. – dada_dave Jul 10 '23 at 20:44
  • "Passing structured bindings is just so very close" You cannot skip an argument or reorder arguments. You can simulate named arguments without these drawbacks, but it's a lot more work. Or you can do [this](https://godbolt.org/z/4bz74PoE6). – n. m. could be an AI Jul 10 '23 at 20:51
  • @Artyer ah of course! thanks for the edit – dada_dave Jul 10 '23 at 20:56
  • @n.m.willseey'allonReddit true it still has some drawbacks, but it is a simple, easy way to do it. I like your example, not sure though that it would work for all the scenarios I have in mind, but maybe. Would have to think about it. – dada_dave Jul 10 '23 at 21:01

1 Answers1

3

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).

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • does `myAB2 ab = {.a = 1, .b = 3};` work because it is an expression? – dada_dave Jul 10 '23 at 21:05
  • 1
    @dada_dave That does CTAD (`{.a=1, .b=3}` is an initializer, not an expression), the same as `auto ab = myAB2{.a = 1, .b = 3}`. – Artyer Jul 10 '23 at 21:42
  • ah so passing `{.a = 1, .b =3}` to `function2` doesn't count as an initializer though for myAB2 even though the parameter it is being passed to is of type myAB2? – dada_dave Jul 10 '23 at 21:48
  • 1
    @dada_dave it would be an initializer, but there's no function overload to be called since `B` can't be deduced – Artyer Jul 10 '23 at 22:33
  • got it, so because B can't be deduced before selecting which function to call, it can't know which potential function2 is the right one without additional information, either or myAB2. So it doesn't necessarily know that the one with myAB2 is the right one and the initializer is for it. I've marked your answer as accepted, thanks! – dada_dave Jul 11 '23 at 09:49