- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
在试用 clang-3.4(从 git 编译)时,它无法编译我的一个项目,提示在解析重载运算符时存在歧义。我发现有两个模板化运算符,其中一个被声明为成员函数,另一个被声明为非成员函数,两者看起来同样匹配。
以下SSCCE演示情况:
#include <iostream>
struct ostr {
std::ostream& s;
template<class T>
ostr& operator<<(const T& x) { s << x; return *this; }
};
struct xy {
double x, y;
};
template<class Stream>
Stream& operator<<(Stream& s, const xy& x) {
s << "[" << x.x << ", " << x.y << "]";
return s;
}
int main() {
ostr os{std::cout};
xy x{4, 5};
os << "Value is: " << x <<"\n";
}
该项目之前编译得很好,我用几个编译器(gcc 4.5
、4.6
、4.7
、4.8
和 clang 3.3
)再次检查了这个 SSCCE,它们都在没有任何警告的情况下编译了它(-Wall -Wextra -pedantic
)。所有编译器都设置为 C++11/C++0x 标准。将 ctor 添加到 ostr
后,即使在 MSVC 2012
和 2010
上也能正常编译)
使两个 operator<<
都成为非成员会在所有编译器中表现出歧义(正如预期的那样)
在查看标准草案(N3242
和 N3690
)后,我没有发现任何使成员函数/运算符比非成员函数/运算符更匹配的东西。
所以我没能证明clang-3.4
是错的,我想知道谁是对的。因此我的问题是:
我知道将第二个 operator<<
更改为非模板函数(使用 std::ostream
而不是模板参数)将解决歧义并按预期工作,但这不是这里的重点。
最佳答案
重载决议为成员函数添加一个额外的参数只是为了重载决议:
[over.match.funcs]/2
The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called.
/4
For non-static member functions, the type of the implicit object parameter is
— “lvalue reference to cv
X
” for functions declared without a ref-qualifier or with the&
ref-qualifier— “rvalue reference to cv
X
” for functions declared with the&&
ref-qualifierwhere
X
is the class of which the function is a member and cv is the cv-qualification on the member function declaration.
遵循一些特殊规则,例如允许将右值绑定(bind)到此隐式对象参数(用于调用不带右值引用限定符的成员函数,例如 ostr{std::cout}<<"hello"
)。
函数签名包括我们需要比较重载决议的隐式对象参数是:
template<class T>
ostr& ostr::operator<<(ostr&, const T&); // F1
template<class Stream>
Stream& ::operator<<(Stream&, const xy&); // F2
替换为 os << x
后,我们得到了相同的签名:
ostr& ostr::operator<<(ostr&, const xy&);
ostr& :: operator<<(ostr&, const xy&);
因此只有 [over.match.best]/1 中的“决胜局”之一可以解决歧义。事实上,可以应用,即“F1
比 F2
更专业”(反之亦然):函数模板的部分排序。
注意添加隐式对象参数的过程在偏序[temp.func.order]/3 的描述中再次指定。
对于 F1
的部分订购和 F2
(如上定义),我们首先创建两个独特的类型:
struct unique_T {};
struct unique_Stream {};
然后我们转换F1
进入F1'
通过替换模板参数 T
具有独特的类型 unique_T
(对于 F2
也是如此):
ostr& ostr::operator<<(ostr&, const unique_T&);
ostr& :: operator<<(unique_Stream&, const xy&);
转换函数的参数F1'
现在用于尝试推断未转换的模板参数 F2
:
ostr a0;
unique_T a1; // no reference, no cv-qualifier
::operator<<(a0, a1) // does template argument deduction succeed?
// reminder: signature of ::operator<<
template<class Stream>
Stream& ::operator<<(Stream&, const xy&);
a0
扣除成功[与Stream
= ostr
],因此类型 ostr&
来自 F1
被认为至少与 F2
的相应第一个参数的类型一样专业( Stream&
, Stream
是模板参数)。我不确定第二个参数会发生什么 a1
, 因为 ::operator<<
的第二个参数没有扣除(它是 const xy&
类型)。
现在我们用来自 F2'
的参数重复这个过程并尝试推导出 F1
的模板参数:
unique_Stream a0;
xy a1;
ostr::operator<<(a0, a1);
// reminder: signature of ostr::operator<<
template<class T>
ostr& ostr::operator<<(ostr&, const T&);
在这里,第一个参数没有推导发生,但第二个参数发生并成功 [with T
= xy
].
我的结论是没有比这更专业的函数模板了。因此,由于歧义,重载解析应该失败。
关于c++ - 模板化运算符重载决议,成员函数与非成员函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19955954/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!