Consider the following code:
#include <tuple>
#include <iostream>
template <class T>
struct custom_wrapper
{
template <class Arg>
custom_wrapper(Arg arg): data(arg) {}
T data;
};
template <class Arg>
custom_wrapper(Arg arg) -> custom_wrapper<Arg>;
template <class... T>
struct custom_tuple
{
template <class... Args>
custom_tuple(Args... args): data(args...) {}
std::tuple<T...> data;
};
template <class... Args>
custom_tuple(Args... args) -> custom_tuple<Args...>;
int main(int argc, char* argv[])
{
custom_wrapper<int> w1(42); // OK
custom_wrapper w2(42); // OK
custom_tuple<int> t1(42); // OK
custom_tuple t2(42); // Fails
return 0;
}
The line that fails return the following error under g++7:
variadic_deduction_guide.cpp: In instantiation of 'custom_tuple<T>::custom_tuple(Args ...) [with Args = {int}; T = {}]':
variadic_deduction_guide.cpp:31:23: required from here
variadic_deduction_guide.cpp:19:45: error: no matching function for call to 'std::tuple<>::tuple(int&)'
custom_tuple(Args... args): data(args...) {}
Is that normal or is that a compiler bug?
This is gcc bug 80871. What follows is an explanation of why the code is well-formed (and clang is correct in deciding that
t2
is acustom_tuple<int>
).The process for figuring out what to do with
basically involves synthesizing a bunch of functions and performing overload resolution on them. The relevant candidates are the synthesized functions from the one constructor and the deduction guide:
From this point it's a choose your own adventure based on your interpretation of what a "trailing parameter pack" is according to [temp.arg.explicit]/3:
T...
isn't trailingThis case is easy. We only have one viable candidate (because
T...
isn't deducible) - the deduction-guide candidate. We deduceArgs...
as{int}
, so we end up withcustom_tuple<int>
.T...
is trailingBoth gcc and clang actually do consider deduction to succeed for the constructor. So we go to the tiebreakers in [over.match.best]:
For purposes of partial ordering, the relevant types are just those which correspond to function parameters, and we're allowed to ignore unused template parameters, so neither function template is considered more specialized than the other.
This leaves us to simply preferring the deduction-guide, which has been the simplest step of this whole process. We deduce
Args...
as{int}
, so we end up withcustom_tuple<int>
.Either way,
custom_tuple<int>
is the correct decision.