这应该编译吗?重载解析和隐式转换 [英] Should this compile? Overload resolution and implicit conversions

查看:106
本文介绍了这应该编译吗?重载解析和隐式转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个例子似乎用VC10和gcc编译(虽然我的gcc版本很老)。



编辑:R. Martinho Fernandez在gcc 4.7该行为仍然相同。

  struct Base 
{
operator double()const {return 0.0 ; }
};

struct foo
{
foo(const char * c){}
};

struct东西:public Base
{
void operator [](const foo& f){}
};

int main()
{
something d;
d [32];

return 0;
}

但俚语抱怨:

  test4.cpp:19:6:错误:使用重载的运算符'[]'是不明确的(操作数类型为Something和const char [3] $ bd [32] 
〜^ ~~~~
test4.cpp:13:10:注意:候选函数
void operator [](const foo& f){}
^
test4.cpp:19:6:note:内置候选运算符[](long,const char *)
d [32]
^
test4.cpp:19:6:注意:内置候选运算符[](long,const restrict char *)
test4.cpp:19:6: volatile char *)
test4.cpp:19:6:注意:内置候选运算符[](long,const volatile restrict char *)

重载解析正在考虑这个表达式考虑两个可能的函数:




  • Something :: operator [](在用户定义的转换后)

  • 为const char *调用内置运算符(认为32[d] double to long)。



如果我写了 d [32] d.operator [](32),那么重载分辨率甚至不会看到选项2,clang也会编译得很好。



编辑:(澄清问题)



这似乎是一个复杂的区域在重载分辨率,非常感谢非常多的答案,详细解释了这种情况下的重载决议,并引用标准(如果有一些模糊/高级可能是未知规则)。



如果cl是正确的,我也有兴趣知道为什么两个是不明确的/一个不是优先于另一个。答案可能需要解释过载解决方案如何考虑两个候选者所涉及的隐式转换(包括用户定义和标准转换),以及为什么一个不优于另一个。



注意:如果将操作符double()更改为操作符bool(),所有三个(clang,vc,gcc)将拒绝编译时出现类似的模糊错误。



<$ c $

c>§13.5.5[over.sub]


因此,下标表达式 x ,将x [y] 解释为 x.operator [](y)如果 T :: operator [](T1)存在,则 c> T


现在,我们先选择一个作为最佳匹配函数的重载解析机制(13.3.3)需要一个过载设置。它根据§13.3.1构造,并且包含成员以及非成员函数。有关详细说明,请参阅我的这个答案



§13.3.1[over.match.funcs]


p2候选函数可以包含要针对相同参数列表解析的成员和非成员函数。因此,在这个异构集中,参数和参数列表是可比较的,成员函数被认为有一个额外的参数,称为隐式对象参数,表示成员函数被调用的对象。 [...]



p3类似地,上下文可以构造一个参数列表,其中包含一个




  //抽象重载集b $ b f1(Something& foo const&); // linked to Something :: operator [](foo const&)
f2(std :: ptrdiff_t,char const *); //链接到operator [](std :: ptrdiff_t,char const *)
f3(char const *,std :: ptrdiff_t); // linked to operator [](char const *,std :: ptrdiff_t)

列表构造:

  //抽象参数列表
(Something& char const [3]某事&'是隐含的对象参数

然后,设置:

  f1  - >参数1上的身份匹配,参数2所需的转换
f2 - >参数1所需的转换,参数2所需的转换(衰减)
f3 - >参数1不兼容,参数2不兼容,丢弃

然后,由于我们发现需要隐式转换,我们来看看§13.3.3[over.match.best] p1


定义 ICSi(F)如下:




  • if F 是一个静态成员函数,[...]否则

  • ICSi(F)表示隐式转换序列,将 i c参数的 i -th类型的列表中的$ c> -th参数。 13.3.3.1定义了隐式转换序列,13.3.3.2定义了一个隐式转换序列比另一个更好的转换序列或更糟的转换序列。


现在让我们为 f1 f2 §13.3.3.1):

  ICS1(f1):'Something&' - > 'something&',标准转换序列
ICS2(f1):'char const [3]' - > 'foo const&',用户定义的转换序列
ICS1(f2):'Something&' - > 'std :: ptrdiff_t',用户定义的转换序列
ICS2(f2):'char const [3]' - > 'char const *',标准转换序列

§13.3.3.2[over .ics.rank] p2


标准转换序列(13.3.3.1.1)序列比用户定义的转换序列或省略号转换序列。


因此 ICS1(f1) code>优于 ICS1(f2) ICS2(f1) ICS2(f2)

相反, ICS1(f2) ICS1 (f1) ICS2(f2) ICS2(f1) / p>

§13.3.3[over.match.best]



给定这些定义,可行函数 F1 被定义为比另一个可行函数更好的函数 F2 如果所有参数 i ICSi(F1)不是 ICSi(F2) 的错误转换序列,然后是[...]



p2如果有一个可行函数是比所有其他可行函数更好的函数,则它是通过重载分辨率选择的函数;


好吧,f * ck。 :)因此,Clang拒绝该代码是正确的。


This example seems to compile with VC10 and gcc (though my version of gcc is very old).

EDIT: R. Martinho Fernandez tried this on gcc 4.7 and the behaviour is still the same.

struct Base
{
    operator double() const { return 0.0; }
};

struct foo
{
    foo(const char* c) {}
};

struct Something : public Base
{
    void operator[](const foo& f) {}
};

int main()
{
    Something d;
    d["32"];

    return 0;
}

But clang complains:

test4.cpp:19:6: error: use of overloaded operator '[]' is ambiguous (with operand types 'Something' and 'const char [3]')
    d["32"]
    ~^~~~~
test4.cpp:13:10: note: candidate function
    void operator[](const foo& f) {}
         ^
test4.cpp:19:6: note: built-in candidate operator[](long, const char *)
    d["32"]
     ^
test4.cpp:19:6: note: built-in candidate operator[](long, const restrict char *)
test4.cpp:19:6: note: built-in candidate operator[](long, const volatile char *)
test4.cpp:19:6: note: built-in candidate operator[](long, const volatile restrict char *)

The overload resolution is considering two possible functions from looking at this expression:

  • calling Something::operator[] (after a user defined conversion)
  • calling built in operator for const char* (think "32"[d]) (after a user defined conversion and standard conversion double to long).

If I had written d["32"] as d.operator[]("32"), then overload resolution won't even look at option 2, and clang will also compile fine.

EDIT: (clarification of questions)

This seems to be a complicated area in overload resolution, and because of that I'd appreciate very much answers that explain in detail the overload resolution in this case, and cite the standard (if there's some obscure/advanced likely to be unknown rule).

If clang is correct, I'm also interested in knowing why the two are ambiguous / one is not preferred over another. The answer likely would have to explain how overload resolution considers implicit conversions (both user defined and standard conversions) involved on the two candidates and why one is not better than the other.

Note: if operator double() is changed to operator bool(), all three (clang, vc, gcc) will refuse to compile with similar ambiguous error.

解决方案

It should be easier to picture why the overload resolution is ambiguous by going through it step-by-step.

§13.5.5 [over.sub]

Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3.3).

Now, we first need an overload set. That's constructed according to §13.3.1 and contains member aswell as non-member functions. See this answer of mine for a more detailed explanation.

§13.3.1 [over.match.funcs]

p2 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. [...]

p3 Similarly, when appropriate, the context can construct an argument list that contains an implied object argument to denote the object to be operated on.

// abstract overload set (return types omitted since irrelevant)
f1(Something&, foo const&); // linked to Something::operator[](foo const&)
f2(std::ptrdiff_t, char const*); // linked to operator[](std::ptrdiff_t, char const*)
f3(char const*, std::ptrdiff_t); // linked to operator[](char const*, std::ptrdiff_t)

Then, an argument list is constructed:

// abstract argument list
(Something&, char const[3]) // 'Something&' is the implied object argument

And then the argument list is tested against every member of the overload set:

f1 -> identity match on argument 1, conversion required for argument 2
f2 -> conversion required for argument 1, conversion required for argument 2 (decay)
f3 -> argument 1 incompatible, argument 2 incompatible, discarded

Then, since we found out that there are implicit conversions required, we take a look at §13.3.3 [over.match.best] p1:

Define ICSi(F) as follows:

  • if F is a static member function, [...]; 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 and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.

Now let's construct those implicit conversion sequences for f1 and f2 in the overload set (§13.3.3.1):

ICS1(f1): 'Something&' -> 'Someting&', standard conversion sequence
ICS2(f1): 'char const[3]' -> 'foo const&', user-defined conversion sequence
ICS1(f2): 'Something&' -> 'std::ptrdiff_t', user-defined conversion sequence
ICS2(f2): 'char const[3]' -> 'char const*', standard conversion sequence

§13.3.3.2 [over.ics.rank] p2

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.

So ICS1(f1) is better than ICS1(f2) and ICS2(f1) is worse than ICS2(f2).
Conversely, ICS1(f2) is worse than ICS1(f1) and ICS2(f2) is better than ICS2(f1).

§13.3.3 [over.match.best]

p1 (cont.) Given these definitions, 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 [...]

p2 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.

Well, f*ck. :) As such, Clang is correct in rejecting that code.

这篇关于这应该编译吗?重载解析和隐式转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆