为什么用户定义的转换比标准整数转换更好?

时间:2014-02-28 00:45:49

标签: c++ templates overload-resolution

您可以在David Vandevoorde和Nicolai Josuttis所着的“C ++模板完整指南”一书的附录B中找到以下内容。

  

B.2简化过载分辨率

     

鉴于这第一个原则,我们留下了具体说明   给定的参数匹配一个可行的相应参数   候选人。作为第一个近似值,我们可以对可能的匹配进行排名   如下(从最佳到最差):

     
      
  • 完美搭配。参数具有表达式的类型,或者它具有对表达式类型的引用的类型(可能   添加了const和/或volatile限定符)。
  •   
  • 与次要调整匹配。这包括,例如,数组变量衰减到指向其第一个元素的指针,或者   添加const以将类型为int **的参数与参数相匹配   输入int const * const *。
  •   
  • 与促销相匹配。促销是一种隐式转换,包括小整数类型的转换(如bool,   char,short,有时是枚举)int,unsigned int,long或   unsigned long,以及float到double的转换。
  •   
  • 仅与标准转化匹配。这包括任何类型的标准转换(例如int到float)但不包括隐式转换   调用转换运算符或转换构造函数。
  •   
  • 与用户定义的转化匹配。这允许任何类型的隐式转换。
  •   
  • 与省略号匹配。省略号参数几乎可以匹配任何类型(但非POD类类型会导致未定义的行为)。
  •   

几页后,本书展示了以下示例和文本(强调我的):

class BadString {
    public:
    BadString(char const*);
    ...
    // character access through subscripting:
    char& operator[] (size_t); // (1)
    char const& operator[] (size_t) const;
    // implicit conversion to null-terminated byte string:
    operator char* (); // (2)
    operator char const* ();
    ...
};
int main()
{
    BadString str("correkt");
    str[5] = 'c'; // possibly an overload resolution ambiguity!
}
  

起初,表达式str [5]似乎没有任何含糊之处。该   (1)的下标运算符似乎是完美的匹配。但是,确实如此   不太完美,因为参数5的类型为int,而且   operator需要一个无符号整数类型(size_t和std :: size_t   通常有unsigned int或unsigned long类型,但从不输入int)。   然而,简单的标准整数转换使得(1)容易生存。   但是,还有另一个可行的候选者:内置下标   运营商。实际上,如果我们将隐式转换运算符应用于str   (这是隐式成员函数参数),我们获得一个指针   类型,现在内置的下标运算符适用。这个内置   operator接受类型为ptrdiff_t的参数,该参数在许多平台上都有   等价于int,因此是参数的完美匹配   5. 因此,即使内置的下标运算符与隐含参数的匹配不佳(通过用户定义的转换),它也是更好的   比(1)中定义的运算符匹配实际的下标!因此   潜在的歧义。

2 个答案:

答案 0 :(得分:0)

请注意,第一个列表是 Simplified Overload Resolution。

要删除int是否与ptrdiff_t相同的“可能”和“潜在”,让我们更改一行:

str[(ptrdiff_t)5] = 'c'; // definitely an overload resolution ambiguity!

现在我从g++收到的消息是:

warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]

--pedantic-errors会将此警告提升为错误。

因此,如果不深入标准,这会告诉您简化列表只是故事的一部分。该列表告诉您从A到B的几个可能的路线中哪一个更喜欢,但它不会告诉您是否更喜欢从A到B从C到D的旅行。

你可以在这里更明显地看到相同的现象(以及相同的g ++消息):

struct S {
    explicit S(int) {}
    operator int() { return 0; }
};

void foo(const S&, long) { }
void foo(int, int) { }

int main() {
    S s(0);
    foo(s, 1);
}

再次调用foo是不明确的,因为当我们选择隐式转换哪个参数以选择重载时,规则说,“选择更轻量级的转换,并转换需要转换的参数“。

现在你要问我标准的引用,但是我会用我需要打干草的事实作为不查找的借口; - )

总之,这里“用户定义的转换比标准整数转换更好”。但是在这种情况下,标准定义的两个可能的重载同样好,因此调用是模糊的。

答案 1 :(得分:0)

str.operator[](size_t(5));

以明确的方式编写代码,你不需要为这些东西烦恼:) 事情还有很多。