考虑我有以下最少的代码:
#include <boost/type_traits.hpp>
template<typename ptr_t>
struct TData
{
typedef typename boost::remove_extent<ptr_t>::type value_type;
ptr_t data;
value_type & operator [] ( size_t id ) { return data[id]; }
operator ptr_t & () { return data; }
};
int main( int argc, char ** argv )
{
TData<float[100][100]> t;
t[1][1] = 5;
return 0;
}
GNU C++ 给了我错误:
test.cpp: In function 'int main(int, char**)':
test.cpp:16: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for second:
test.cpp:9: note: candidate 1: typename boost::remove_extent<ptr_t>::type& TData<ptr_t>::operator[](size_t) [with ptr_t = float [100][100]]
test.cpp:16: note: candidate 2: operator[](float (*)[100], int) <built-in>
我的问题是:
- 为什么 GNU C++ 给出错误,而 Intel C++ 编译器却没有?
- 为什么把
operator[]
改成下面这样会导致编译没有错误?value_type & operator [] ( int id ) { return data[id]; }
感谢 C++ 标准的链接。
我可以看到这里有两种转化路径:
- (1)
int
到size_t
和 (2)operator[](size_t)
。 - (1)
operator ptr_t&()
,(2)int
到size_t
和 (3)内置operator[ ](size_t)
.
最佳答案
其实很简单。对于t[1]
, 重载决议有以下候选:
候选 1(内置:13.6/13)(T 是任意对象类型):
- 参数列表:
(T*, ptrdiff_t)
候选人 2(您的接线员)
- 参数列表:
(TData<float[100][100]>&, something unsigned)
参数列表由 13.3.1.2/6
给出:
The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates. The argument list contains all of the operands of the operator.
- 参数列表:
(TData<float[100][100]>, int)
您看到第一个参数与候选 2 的第一个参数完全匹配。但它需要对候选 1 的第一个参数进行用户定义的转换。因此对于第一个参数,第二个候选者获胜。
您还看到第二个位置的结果取决于。让我们做一些假设,看看我们得到了什么:
-
ptrdiff_t
是int
:第一个候选者获胜,因为它具有完全匹配,而第二个候选者需要整数转换。 -
ptrdiff_t
是long
: 两个候选人都没有获胜,因为两者都需要积分转换。
现在,13.3.3/1
说
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.
A viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then ... for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that ...
对于我们的第一个假设,我们没有获得总冠军,因为候选人 2 赢得了第一个参数,而候选人 1 赢得了第二个参数。我称之为纵横交错。对于我们的第二个假设,候选 2 总体上获胜,因为这两个参数的转化率都较差,但第一个参数的转化率更好。
对于第一个假设,第二个参数中的整数转换(int 到 unsigned)与用户定义的第一个参数中另一个候选者的转换相比,这并不重要。在纵横交错中,规则是粗糙的。
最后一点可能会让你感到困惑,因为大家都在大惊小怪,所以让我们举个例子
void f(int, int) { }
void f(long, char) { }
int main() { f(0, 'a'); }
这给了你同样令人困惑的 GCC 警告(我记得,当我几年前第一次收到它时,它实际上把我搞糊涂了),因为 0
转换为 long
比 'a'
差至int
- 但是你会得到一个模棱两可的地方,因为你是在一个纵横交错的情况下。
关于c++ - 为什么这里会出现这种歧义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3519282/