重载解析和数组:应该调用哪个函数?
考虑以下程序:
#include <cstddef>
#include <cstdio>
void f(char const*&&) { std::puts("char const*&&"); } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)
template <std::size_t N>
void f(char const (&)[N]) { std::puts("char const(&)[N]"); } // (3)
int main()
{
const char data[] = "a";
f(data);
}
应该调用哪个f
?为什么?
三个编译器的最新发布版本对这个问题的答案不一致:
- 使用g++ 4.5.2编译程序时会调用(1)
- 使用 Visual C++ 2010 SP1 编译程序时调用 (2)
- (3) 使用 编译程序时调用 (3) Clang 3.0 (trunk 127530)
不同的 C++0x 草案中的重载解析规则是否发生了重大变化?或者,其中两个编译器真的完全错误吗?根据最新的 C++0x 草案,哪个重载是正确选择的重载?
Consider the following program:
#include <cstddef>
#include <cstdio>
void f(char const*&&) { std::puts("char const*&&"); } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)
template <std::size_t N>
void f(char const (&)[N]) { std::puts("char const(&)[N]"); } // (3)
int main()
{
const char data[] = "a";
f(data);
}
Which f
should be called? Why?
The latest released versions of three compilers disagree on the answer to this question:
- (1) is called when the program is compiled using g++ 4.5.2
- (2) is called when the program is compiled using Visual C++ 2010 SP1
- (3) is called when the program is compiled using Clang 3.0 (trunk 127530)
Have the overload resolution rules changed substantially in different C++0x drafts? Or, are two of these compilers really just completely wrong? Which overload is the correct overload to be selected per the latest C++0x draft?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先,所有三个的转换序列是相同的,除了前两个有一个左值转换(左值到右值转换),但它不用于排序转换序列。这三个都是完全匹配的(函数模板特化具有参数类型
char const(&)[2]
)。如果您迭代
13.3.3.2p3
处的规则,则会在本段处停止如果需要将右值引用绑定到左值,则无法形成转换序列,规范在 13.3.3.1.4p3 中表示。如果您查看引用绑定在 8.5.3p5 最后一个项目符号中的工作原理,它将从数组创建一个
char const*
类型的临时(我认为他们的意思是rvalue临时)左值并将引用绑定到该临时值。因此,我认为(1)
比(2)
更好。同样适用于(1)
与(3)
,尽管我们不需要这个,因为(3)
是一个模板,所以处于平局,我们会再次选择(1)
。在n3225中,他们更改了引用绑定规则,以便右值引用可以绑定到左值的初始化表达式,只要该引用将绑定到右值(可能是通过之前正确转换初始化器创建的) 。这可能会影响 Visual C++ 的处理,这里可能不是最新的。
我不确定铿锵声。即使它会忽略
(1)
,那么它最终也会处于(2)
和(3)
之间的平局,并且需要选择(2)
因为它是非模板。我认为 8.5.3p5 最后一个项目符号很令人困惑,因为它说“否则是临时类型......”。目前尚不清楚临时文件是否被 13.3.3.1.4p3 视为左值或右值,这意味着我不确定
如果我们假设临时文件被处理,那么 根据规范的确切文字,以下内容应该如何真正表现作为第 13 条的右值,然后我们将右值引用绑定到第二个函数中的右值和第一个函数中的左值。因此,我们将选择第二个函数,然后通过 8.5.3p5 最后一个项目符号进行诊断,因为
T1
和T2
是引用相关的。如果我们假设第 13 条将临时变量视为左值,则以下内容将不起作用,因为我们将右值引用绑定到第 13 条的左值,这将使该函数不可行。如果我们将“将右值引用绑定到左值”解释为引用初始化表达式而不是绑定到的最终表达式,我们将不会接受以下内容,
但这从 n3225 开始是有效的。所以似乎有些混乱 - 我就此向委员会发送了一份 DR。
First, the conversion sequence of all three is the same, except that for the first two, there is an lvalue transformation (lvalue to rvalue conversion), which however is not used in ordering conversion sequences. All three are exact matches (the function template specialization has parameter type
char const(&)[2]
).If you iterate over the rules at
13.3.3.2p3
, you stop at this paragraphA conversion sequence cannot be formed if it requires binding an rvalue reference to an lvalue, the spec says at 13.3.3.1.4p3. If you look at how reference binding works at 8.5.3p5 last bullet, it will create a temporary (I think they meant rvalue temporary) of type
char const*
from the array lvalue and bind the reference to that temporary. Therefor, I think(1)
is better than(2)
. Same holds for(1)
against(3)
, although we wouldn't need this because(3)
is a template so in a tie, we would choose(1)
again.In
n3225
, they changed the reference binding rules so that rvalue references can bind to initializer expressions that are lvalues, as long as the reference will be bound to an rvalue (possibly created by converting the initializer properly before). This could influence the handling by Visual C++, which may not be up to date here.I'm not sure about clang. Even if it would ignore
(1)
, then it would end up in a tie between(2)
and(3)
, and would need to choose(2)
because it's a non-template.I think that 8.5.3p5 last bullet is confusing because it says "Otherwise a temporary of type ..". It's not clear whether the temporary is regarded as an lvalue or as an rvalue by 13.3.3.1.4p3, which means I'm not sure how the following should really behave according to the exact words of the spec
If we assume the temporary is treated as an rvalue by clause 13, then we bind an rvalue ref to an rvalue in the second function and an lvalue in the first. Therefor, we will choose the second function and then get a diagnostic by 8.5.3p5 last bullet because
T1
andT2
are reference-related. If we assume the temporary is treated as an lvalue by clause 13, then the following would not workBecause we would bind an rvalue ref to an lvalue which by clause 13 will make the function non-viable. And if we interpret "binding an rvalue ref to an lvalue" to refer to the initializer expression instead of the final expression bound to, we won't accept the following
This however is valid as of n3225. So there seems to be some confusion - I sent a DR to the committee about this.
我声称 #3 是由符合标准的编译器选择的函数。
(1) 比 (2) 更好,因为“如果 S1 和 S2 是引用绑定 (8.5.3) 并且都不引用非静态的隐式对象参数,则标准转换序列 S1 是比标准转换序列 S2 更好的转换序列。声明的成员函数没有引用限定符,S1 将右值引用绑定到右值,S2 绑定左值引用。”
(3) 比 (1) 和 (2) 都好,因为它是恒等转换(其他都是精确匹配转换)并且“如果 S1 是真子序列,则标准转换序列 S1 是比标准转换序列 S2 更好的转换序列” S2的(比较13.3.3.1.1定义的规范形式的转换序列,不包括任何左值变换;恒等转换序列被认为是任何非恒等转换序列的子序列)”
模板与非模板仅考虑到两种转换都不是更好的情况“或者,如果不是那样……”,
但奇怪的是,科莫更喜欢 (2) 而不是 (3)。该测试用例无法编译:
I claim that #3 is the function chosen by a conforming compiler.
(1) is better than (2) because "Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference."
(3) is better than both (1) and (2) because it is an identity conversion (the others are exact match conversions) and "Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence)"
Template vs non-template is only considered when neither conversion is better "or, if not that..."
oddly enough though, Comeau prefers (2) over (3). This test case fails to compile:
这是一个社区 wiki 答案,用于收集标准(草案 3225)中的片段。
第 13.3.3 节“最佳可行函数”
[over.match.best]
定义 ICSi(F) 如下:
如果
F
是静态成员函数,则 ICS1(F) 的定义使得 ICS1(F) 既不比 ICS1(F) 更好也不比ICS1(G) 对于任何函数
G
,并且对称地,ICS1(G) 既不比 ICS1(F) 更好也不比 ICS1(F) 更差;否则,令 ICSi(F) 表示隐式转换序列,将列表中的第 i 个参数转换为
可行函数
F
的第i个参数的类型。 13.3.3.1 定义隐式转换序列和13.3.3.2 定义了一个隐式转换序列比另一个隐式转换序列更好或更差的含义。
给定这些定义,如果对于所有参数i,则可行函数
F1
被定义为比另一个可行函数F2
更好的函数, ICSi(F1) 并不是比 ICSi(F2) 更差的转换序列,然后或者,如果不是这样,
F1
的返回类型到目标类型的标准转换序列进行的初始化(即,正在初始化的实体的类型)是比从F2
返回类型到目标类型的标准转换序列更好的转换序列或者,如果不是这样,
F1
是非模板函数,F2
是函数模板特化或者,如果不是这样,
F1
和F2
是函数模板特化,F1
的函数模板更加特化根据 14.5.6.2 中描述的部分排序规则,与
F2
的模板不同。如果恰好有一个可行函数比所有其他可行函数都更好,那么它就是重载决议选择的函数;
第 13.3.3.1.4 节“引用绑定”
[over.ics.ref]
当引用类型的参数直接 (8.5.3) 绑定到参数时表达式、隐式转换
序列是恒等转换,除非参数表达式的类型是该序列的派生类
参数类型,在这种情况下,隐式转换序列是派生到基数的转换 (13.3.3.1)。如果参数直接绑定到将转换函数应用于
参数表达式,隐式转换序列是用户定义的转换序列(13.3.3.1.2),第二个标准转换序列可以是恒等转换,或者如果转换函数
返回类型的实体,该类型是参数类型的派生类,即派生到基的转换。
当引用类型的参数没有直接绑定到参数表达式时,转换顺序
是将参数表达式转换为引用的基础类型所需的表达式
至 13.3.3.1。从概念上讲,此转换序列对应于复制初始化临时的
带有参数表达式的基础类型。顶级简历资格的任何差异都包含在
初始化本身并不构成转换。
第 13.3.3.2 节“对隐式转换序列进行排名”
[over.ics.rank]
13.3.3.2 基于更好的关系定义了隐式转换序列的部分排序转换
顺序和更好的转换。如果隐式转换序列
S1
由这些规则定义为更好的转换序列比
S2
更差,那么同样情况下S2
是比S1
更差的转换序列。如果转换序列
S1
既不比转换序列S2
更好也不更差,据说S1
和S2
是不可区分的转换序列。
比较隐式转换序列的基本形式(如 13.3.3.1 中定义)
标准转换序列 (13.3.3.1.1) 是比用户定义的转换序列或省略号转换序列更好的转换序列,并且
用户定义的转换序列 (13.3.3.1.2) 是比省略号转换序列 (13.3.3.1.3) 更好的转换序列。
两个相同形式的隐式转换序列是无法区分的转换序列,除非其中之一
以下规则适用:
标准转换序列
S1
是比标准转换序列S2
更好的转换序列,如果或者,如果不是这样,
S1
的排名优于S2
的排名,或者S1
和S2
的排名相同排名并可通过以下段落中的规则进行区分或者,如果不是这样,
S1
和S2
仅在限定转换方面有所不同,并产生类似的类型T1
和T2
(4.4)分别,T1
类型的 cv 限定签名是T2
类型的 cv 限定签名的真子集。或者,如果不是这样,
S1
和S2
是引用绑定 (8.5.3),并且都不引用 a 的隐式对象参数声明时没有引用限定符的非静态成员函数,并且
S1
将右值引用绑定到右值和
S2
绑定左值引用。This is a community wiki answer for collecting snippets from the standard (draft 3225).
section 13.3.3 "Best viable function"
[over.match.best]
Define ICSi(F) as follows:
if
F
is a static member function, ICS1(F) is defined such that ICS1(F) is neither better nor worse thanICS1(G) for any function
G
, and, symmetrically, ICS1(G) is neither better nor worse than ICS1(F); otherwise,let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the
type of the i-th parameter of viable function
F
. 13.3.3.1 defines the implicit conversion sequences and13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.
Given these definitions, a viable function
F1
is defined to be a better function than another viable functionF2
if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and thenor, if not that,
F1
to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type ofF2
to the destination typeor, if not that,
F1
is a non-template function andF2
is a function template specializationor, if not that,
F1
andF2
are function template specializations, and the function template forF1
is more specializedthan the template for
F2
according to the partial ordering rules described in 14.5.6.2.If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.
section 13.3.3.1.4 "Reference binding"
[over.ics.ref]
When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion
sequence is the identity conversion, unless the argument expression has a type that is a derived class of the
parameter type, in which case the implicit conversion sequence is a derived-to-base conversion (13.3.3.1). If the parameter binds directly to the result of applying a conversion function to the
argument expression, the implicit conversion sequence is a user-defined conversion sequence (13.3.3.1.2), with the second standard conversion sequence either an identity conversion or, if the conversion function
returns an entity of a type that is a derived class of the parameter type, a derived-to-base Conversion.
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence
is the one required to convert the argument expression to the underlying type of the reference according
to 13.3.3.1. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the
underlying type with the argument expression. Any difference in top-level cv-qualification is subsumed by
the initialization itself and does not constitute a conversion.
section 13.3.3.2 "Ranking implicit conversion sequences"
[over.ics.rank]
13.3.3.2 defines a partial ordering of implicit conversion sequences based on the relationships better conversion
sequence and better conversion. If an implicit conversion sequence
S1
is defined by these rules to be a betterconversion sequence than
S2
, then it is also the case thatS2
is a worse conversion sequence thanS1
. Ifconversion sequence
S1
is neither better than nor worse than conversion sequenceS2
,S1
andS2
are said tobe indistinguishable conversion sequences.
When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion sequence (13.3.3.1.3).
Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of
the following rules applies:
Standard conversion sequence
S1
is a better conversion sequence than standard conversion sequenceS2
ifor, if not that,
S1
is better than the rank ofS2
, orS1
andS2
have the same rank and are distinguishable by the rules in the paragraph belowor, if not that,
S1
andS2
differ only in their qualification conversion and yield similar typesT1
andT2
(4.4), respectively, and the cv-qualification signature of typeT1
is a proper subset of the cv-qualification signature of typeT2
.or, if not that,
S1
andS2
are reference bindings (8.5.3) and neither refers to an implicit object parameter of anon-static member function declared without a ref-qualifier, and
S1
binds an rvalue reference toan rvalue and
S2
binds an lvalue reference.