Kir*_*sky 35 c++ templates operator-overloading
考虑我有以下最小代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
我的问题是:
operator[]为以下内容会导致编译没有错误?
value_type & operator [] ( int id ) { return data[id]; }
欢迎链接到C++标准.
我在这里看到两个转换路径:
int至size_t和(2)operator[](size_t).operator ptr_t&(),(2)int到size_t和(3)内置operator[](size_t).Joh*_*itb 32
这实际上很直接.因为t[1],重载决议有这些候选人:
候选人1(内置:13.6/13)(T是一些任意对象类型):
(T*, ptrdiff_t)候选人2(您的运营商)
(TData<float[100][100]>&, something unsigned)参数列表由下式给出13.3.1.2/6:
重载决策的候选函数集是成员候选者,非成员候选者和内置候选者的联合.参数列表包含运算符的所有操作数.
(TData<float[100][100]>, int)您会看到第一个参数与Candidate 2的第一个参数完全匹配.但它需要用户定义的转换为Candidate 1的第一个参数.因此对于第一个参数,第二个候选获胜.
你还看到第二个位置的结果取决于.让我们做一些假设,看看我们得到了什么:
ptrdiff_t是int:第一个候选人获胜,因为它具有完全匹配,而第二个候选人需要进行积分转换.ptrdiff_t是long:两个候选人都没有获胜,因为两者都需要进行积分转换.现在,13.3.3/1说
令ICSi(F)表示将列表中的第i个参数转换为可行函数F的第i个参数的类型的隐式转换序列.
如果对于所有参数i,可行函数F1被定义为比另一个可行函数F2更好的函数,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后......对于某些参数j,ICSj( F1)是比ICSj(F2)更好的转换序列,或者,如果不是......
对于我们的第一个假设,我们没有获得总冠军,因为候选人2赢得第一个参数,候选人1赢得第二个参数.我称之为十字交叉.对于我们的第二个假设,候选人2总体上获胜,因为两个参数都没有更差的转换,但第一个参数有更好的转换.
对于第一个假设,第二个参数中的积分转换(int到unsigned)与第一个参数中另一个候选的用户定义转换相比,并不重要.在十字路口,规则是粗糙的.
最后一点可能仍然会让你感到困惑,因为所有的大惊小怪,所以让我们举个例子
void f(int, int) { }
void f(long, char) { }
int main() { f(0, 'a'); }
Run Code Online (Sandbox Code Playgroud)
这给你同样的困惑GCC警告(我记得它,实际上是混淆了地狱了我当我第一次在几年前接受了它),因为0皈依long逊于'a'到int-但你得到的不确定性,因为你是在一个纵横交错的情况.
Dav*_*eas 13
用表达式:
t[1][1] = 5;
Run Code Online (Sandbox Code Playgroud)
编译器必须集中在左侧以确定其中的内容,因此= 5;在lhs解析之前将被忽略.让我们使用表达式:t[1][1],表示两个操作,第二个操作对第一个结果进行操作,因此编译器必须只考虑表达式的第一部分:t[1].实际类型是(TData&)[(int)]
呼叫不完全匹配的任何功能,如operator[]对TData被定义为取一个size_t参数,因此能够使用它的编译器必须转换1来自int于size_t同隐式转换.这是第一选择.现在,另一种可能的路径是应用用户定义的转换来转换TData<float[100][100]>为float[100][100].
的int到size_t转换是一个整体的转换被列为转换在标准的表9中,由于是用户定义的转换从TData<float[100][100]>到float[100][100]转换根据§13.3.3.1.2/ 4.从转换float [100][100]&到float (*)[100]被列为完全匹配于表9中的编译器是不允许来自这两个转换序列进行选择.
Q1:并非所有编译器都以相同的方式遵守标准.很常见的是,在某些特定情况下,编译器的执行速度与其他编译器不同.在这种情况下,g ++实现者决定抱怨不允许编译器选择的标准,而英特尔实现者可能只是默默地应用他们的首选转换.
Q2:当您更改用户定义的签名时operator[],参数与传入的类型完全匹配.t[1]完全匹配t.operator[](1)没有任何转换,因此编译器必须遵循该路径.