具有显式模板参数列表和 [temp.arg.explicit]/3 的函数调用推导失败
Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3
[temp.arg.explicit]/3 of the C++17 standard (final draft) 说关于使用明确指定的模板参数列表推导函数模板参数:
In contexts where deduction is done and fails, or [...], if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
这如何应用于参数包?
考虑
template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}
这可以在 MSVC 上编译,但不能在 GCC 和 Clang 上编译,请参阅 godbolt。我的直觉也是它应该失败,因为推导会失败,但上面的引用似乎暗示即使推导失败,因为 f<int>
(在我的理解中)唯一标识模板专业化, f<int>
应该被认为是引用该专业化然后调用它,没有重载决议,这将起作用,隐式地将 0
转换为 S<int>
.
我对引用的理解有什么问题,或者 MSVC 确实正确吗?
请注意,如果我们尝试调用 f<>(0);
(根据上述考虑,我认为这应该可行)所有三个编译器都拒绝编译。
有一个与此案例密切相关的开放核心语言问题 (issue 2055: Explicitly-specified non-deduced parameter packs)。
根据我对这个问题中隐含陈述的理解,其意图是编译器应该像 MSVC 一样运行,但据说标准不够明确。
与该问题相关的还有 [temp.arg.explicit]/6,它告诉我们函数参数的隐式转换(如您所愿)是
if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] ]
所以,现在的问题是A...
是否参与模板参数推导。 (此时我想指出,如果我们用一个模板参数替换参数包,OPs 代码也会在 gcc/clang 下编译,因为它应该是明确指定的)。
有人可能会争辩说 A...
是明确指定的,因此不参与推导。但我认为有人错了。 [temp.arg.explicit]/9 告诉我们演绎可以扩展明确指定的模板参数列表。因此,f<int>(S<int, char>{0});
是有效的并且 A...
被推导为 int, char
。所以这种情况下A...
肯定是参与推演的。但由于此调用仅在参数上与您的调用不同,因此也必须在您的调用中进行推导。
换句话说,f<int>(0);
也可能意味着调用 f<int, char>
,因此,它不指定单个函数模板规范。
这无关紧要。没有“没有重载决议”的函数调用之类的东西。 CWG2092 说明了这一点。
[temp.over]/1 控件(为了便于阅读而分开;强调我的):
When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments.
For each function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution.
If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template. The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].
[temp.arg.explicit]/3 of the C++17 standard (final draft) 说关于使用明确指定的模板参数列表推导函数模板参数:
In contexts where deduction is done and fails, or [...], if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
这如何应用于参数包?
考虑
template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}
这可以在 MSVC 上编译,但不能在 GCC 和 Clang 上编译,请参阅 godbolt。我的直觉也是它应该失败,因为推导会失败,但上面的引用似乎暗示即使推导失败,因为 f<int>
(在我的理解中)唯一标识模板专业化, f<int>
应该被认为是引用该专业化然后调用它,没有重载决议,这将起作用,隐式地将 0
转换为 S<int>
.
我对引用的理解有什么问题,或者 MSVC 确实正确吗?
请注意,如果我们尝试调用 f<>(0);
(根据上述考虑,我认为这应该可行)所有三个编译器都拒绝编译。
有一个与此案例密切相关的开放核心语言问题 (issue 2055: Explicitly-specified non-deduced parameter packs)。
根据我对这个问题中隐含陈述的理解,其意图是编译器应该像 MSVC 一样运行,但据说标准不够明确。
与该问题相关的还有 [temp.arg.explicit]/6,它告诉我们函数参数的隐式转换(如您所愿)是
if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] ]
所以,现在的问题是A...
是否参与模板参数推导。 (此时我想指出,如果我们用一个模板参数替换参数包,OPs 代码也会在 gcc/clang 下编译,因为它应该是明确指定的)。
有人可能会争辩说 A...
是明确指定的,因此不参与推导。但我认为有人错了。 [temp.arg.explicit]/9 告诉我们演绎可以扩展明确指定的模板参数列表。因此,f<int>(S<int, char>{0});
是有效的并且 A...
被推导为 int, char
。所以这种情况下A...
肯定是参与推演的。但由于此调用仅在参数上与您的调用不同,因此也必须在您的调用中进行推导。
换句话说,f<int>(0);
也可能意味着调用 f<int, char>
,因此,它不指定单个函数模板规范。
这无关紧要。没有“没有重载决议”的函数调用之类的东西。 CWG2092 说明了这一点。
[temp.over]/1 控件(为了便于阅读而分开;强调我的):
When a call to the name of a function or function template is written (explicitly, or implicitly using the operator notation), template argument deduction ([temp.deduct]) and checking of any explicit template arguments ([temp.arg]) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments.
For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution.
If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in [over.match.best].