英文:
Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3
问题
[temp.arg.explicit]/3 关于带有显式指定的模板参数列表的函数模板参数的推导的C++17标准(最终草案)中的内容如下:
> 在进行推导并失败的情况下,或者[...],如果指定了模板参数列表,并且它以及任何默认模板参数确定了单个函数模板特化,那么模板标识符是该函数模板特化的lvalue。
这如何适用于参数包?
考虑以下代码:
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);`(我猜应该按照上述考虑是有效的),所有三个编译器都会拒绝编译。
英文:
[temp.arg.explicit]/3 of the C++17 standard (final draft) says about deduction of function template arguments with explicitly specified template argument lists:
> 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.
How does this apply to parameter packs?
Consider
template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}
This compiles on MSVC, but not on GCC and Clang, see godbolt. It would also be my intuition that it should fail, because deduction will fail, but the quote above seems to imply that even if deduction fails, since f<int>
(in my understanding) identifies uniquely a template specialization, f<int>
should be considered to refer to that specialization and then call it, without overload resolution, which will work, implicitly converting 0
to S<int>
.
What is wrong in my understanding of the quote or is MSVC indeed correct?
Note that if we try to call f<>(0);
(which I guess should work by the considerations above) all three compilers refuse to compile.
答案1
得分: 3
与问题相关的是[temp.arg.explicit]/6 ,它告诉我们,对函数参数的隐式转换(如您上面所需)是
如果参数类型不包含参与模板参数推断的模板参数,则
> [注意:如果明确指定,模板参数不会参与模板参数推断。[...]]
所以,现在的问题是 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>
,因此它不指定单个函数模板规范。
英文:
Relevant for the question is also [temp.arg.explicit]/6 that tells us that implicit conversions on a function parameter (as you want above) are
> 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. [...] ]
So, now is the question if A...
participates in template argument deduction. (At this point I wanto to note that OPs code compiles also under gcc/clang if we replace the parameter pack by one template parameter, as it should since it is explicitly specified).
One could argue that A...
is explicitly specified and therefore does not participate in deduction. But I would argue that one is wrong. [temp.arg.explicit]/9 tells us that deduction can extend explicitly specified template argument lists. Hence, f<int>(S<int, char>{0});
is valid and A...
is deduced to int, char
. So in this case A...
definitely participates in deduction. But since this call only differs from your call by the parameter, the deduction also has to take place in your call, too.
In other words f<int>(0);
could also mean to call f<int, char>
and as such, it does not specify a single function template specification.
答案2
得分: 2
这与问题无关。没有所谓的“没有重载解析”的函数调用。CWG2092明确了这一点。
[temp.over]/1 控制(为了易读性而分开; 强调是我的):
当编写对函数或函数模板名称的调用时(明确地或使用操作符表示法隐式地),将为每个函数模板执行模板参数推导([temp.deduct])和任何显式模板参数([temp.arg])的检查,以查找可以与该函数模板一起使用的模板参数值(如果有的话),以实例化可以使用调用参数调用的函数模板特化。
对于每个函数模板,如果参数推导和检查成功,则使用模板参数(推导和/或显式)来合成单个函数模板特化的声明,该声明添加到候选函数集中以用于重载解析。
如果对于给定的函数模板,参数推导失败或合成的函数模板特化将无效,则不会将这种函数添加到该模板的候选函数集中。 候选函数的完整集合包括所有合成的声明以及相同名称的所有非模板重载函数。合成的声明在重载解析的其余部分中像任何其他函数一样处理,除非在[over.match.best]中明确说明。
英文:
It's irrelevant. There's no such thing as a function call "without overload resolution". CWG2092 makes this clear.
[temp.over]/1 controls (broken apart for readability; emphasis mine):
> 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].
答案3
得分: 1
有一个开放的核心语言问题(问题2055:明确定义的未推导参数包1),与这个案例密切相关。
根据我对这个问题中隐含的陈述的理解,意图是编译器应该表现得像MSVC一样,但标准被认为不够清晰。
英文:
There is an open core language issue (issue 2055: Explicitly-specified non-deduced parameter packs) that is well related to this case.
From my understanding of what is implictly stated in this issue, the intent is that compilers should behave as MSVC but the standard is said not to be clear enough.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论