The following code compiled well in trunk gcc and clang, but failed in msvc for c 20 mode:
template <typename T = int>
void f(void* ptr, T&& t = {}) {
if (ptr) {
f(nullptr);
}
}
with the messages:
error C2672: 'f': no matching overloaded function found note: could be 'void f(void *,T &&)' note: 'void f(void *,T &&)': could not deduce template argument for 'T' note: 'f': function declaration must be available as none of the arguments depend on a template parameter
Works well for msvc for c 17 mode, since /permissive-
is only available by default since c 20.
Can be easily fixed for msvc c 20 by specifying template type explicitly, i.e. f<T>(nullptr);
in the function body.
Who is right according to the standard? I'm interested for both c 17 and c 20 modes. Is there any changes will come with upcoming c 23?
CodePudding user response:
This is CWG 2608 and the program is well-formed and gcc and clang are standard conformant.
If all of the template arguments can be deduced or obtained from default template-arguments, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.
(emphasis mine)
This means that f(nullptr);
is well-formed as the template argument for T
can be obtained from the default template argument and hence it may be omitted which implies that the empty template argument list <> itself may also be omitted. Thus, gcc and clang are standard conformant. Note also that msvc compiles this with /permissive
so there is no reason to submit a bug report since they usually advise to use /permissive
flag before submitting bugs.
You can also confirm this by adding empty <>
and you will notice that msvc then compiles the program. Demo
template <typename T = int>
void f(void* ptr, T&& t = {}) {
if (ptr) {
//-------vv-------------->msvc compiles this with empty <> - See CWG 2608
f<>(nullptr);
}
}
CodePudding user response:
This is a bug with MSVC.
It is contrary to [dcl.fct.default]p9:
When an overload set contains a declaration of a function that inhabits a scope S, any default argument associated with any reachable declaration that inhabits S is available to the call.
Where the associated default arguments are reachable as soon as they are declared.
MSVC does not seem to be able to find the default arguments directly after they are declared, but only after the entire definition of the function.
When you split off the declaration for the defaults:
template <typename T = int>
void f(void* ptr, T&& t = {});
template <typename T>
void f(void* ptr, T&& t) {
if (ptr) {
f(nullptr);
}
}
(Or turn off MSVC's two phase lookup with /Zc:twoPhase-
, or "trick" the two-phase lookup by adding a dummy using ::f;
or writing f<>(nullptr)
instead)
Then the declaration for the default arguments can be seen and it will be called properly.