As part of a bigger project, I'm playing with std::tuple
and templates; consider the following code:
template <typename ...T> void foo(tuple<T...> t) {}
void bar(tuple<int, char> t) {}
tuple<int, char> quxx() { return {1, 'S'}; }
int main(int argc, char const *argv[])
{
foo({1, 'S'}); // error
foo(make_tuple(1, 'S')); // ok
bar({1, 'S'}); // ok
quxx(); // ok
return 0;
}
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error (GCC 7.2.0):
main.cpp: In function 'int main(int, const char**)':
main.cpp:14:17: error: could not convert '{1, 'S'}' from '<brace-enclosed initializer list>' to 'std::tuple<>'
foo({1, 'S'}); // error
^
Is there any way I can use brace-enclosed syntax in this scenario?
Some Context : this is going to be used in an operator overload so I guess I'm bound to tuples and cannot make use of variadics, any hint is well-accepted.
Extra : Clang 6 also complains
prog.cc:12:5: error: no matching function for call to 'foo'
foo({1, 'S'}); // error
^~~
prog.cc:6:31: note: candidate function [with T = <>] not viable: cannot convert initializer list argument to 'tuple<>'
template <typename ...T> void foo(tuple<T...> t) {}
The problem is another.
When you call
bar({1, 'S'})
, the compiler knows thatbar()
receive atuple<int, char>
, so take1
asint
and'S'
aschar
.See another example: if you define
you can call
because the compiler knows that
baz()
receive astd::tuple<int>
so take1
to initialize theint
in the tuple.But with
the compiler doesn't know the
T...
types; when you callwhat
T...
types should deduce the compiler?I see, at least, two hypothesis:
T = int, char
orT = std::pair<int, char>
; or alsoT = std::tuple<int, char>
.Which hypothesis should follows the compiler?
I mean: if you pass a
std::tuple
tofoo()
, the compiler accept the list of types in the tuple as the list ofT...
; but if you pass something else, the compiler must deduce the correctstd::tuple
; but this deduction, in this case, is not unique. So the error.A braced-init-list, like
{1, 'S'}
, does not actually have a type. In the context of template deduction, you can only use them in certain cases - when deducing againstinitializer_list<T>
(whereT
is a function template parameter) or when the corresponding parameter is already deduced by something else. In this case, neither of those two things is true - so the compiler cannot figure out what...T
is supposed to be.So you can provide the types directly:
Or you can construct the
tuple
yourself and pass that in:Today,
ClassTemplate<Ts...>
can only be deduced from expressions of typeClassTemplate<Us...>
or types that inherit from something like that. A hypothetical proposal could extend that to additionally try to perform class template argument deduction on the expression to see if that deduction succeeds. In this case,{1, 'S'}
isn't atuple<Ts...>
buttuple __var{1, 'S'}
does successfully deducetuple<int, char>
so that would work. Such a proposal would also have to address issues like... what if we're deducingClassTemplate<T, Ts...>
or any minor variation, which isn't something that class template argument deduction allows (but is something that many people have at times expressed interest in being able to do).I'm not aware of such a proposal today.