在运算符情况下的C++ const转换

7

考虑以下代码:

struct A {
    void operator++() const {}
};

void operator++(const A&) {}


int main () {
    const A ca;
    ++ca; // g++ Error (as expected): ambiguous overload for ‘operator++’

    A a;
    ++a; // 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"
         // candidate 1: void operator++(const A&)
         // candidate 2: void A::operator++() const
}

为什么g++只发出警告而不是错误提示 ++a ?换句话说,非成员函数如何比成员函数更适合?
谢谢!

2
g++ 基本上认为这种歧义可以安全地解决,尽管标准表示否定;如果您添加 -pedantic-errors,它应该会报错。 - Collin Dauphinee
我认为你提到的评论与此处发生的事情无关,因为你的示例中没有转换。相反,我认为gcc消息与隐式对象参数暗示对象参数有关,但我不太确定它与非成员函数的实际参数(在标准中)的区别在哪里。 - dyp
浏览类似错误信息的问题,似乎它(通常)指的是至少有两个参数的情况,例如https://dev59.com/DnA75IYBdhLWcg3wAUJ9#3525172。 - dyp
@dyp 我指的是我链接的评论,其中提到“编译器必须创建一个临时的DepthDescriptor对象。这是C++的规则,你不能将临时对象绑定到非const引用。”但我在网上找不到更多信息。谢谢你的链接,我会查看它的。 - bap
1
在那个问题的上下文中,是的,它必须创建一个临时变量,以便进行所需的转换(从枚举类型到类类型)。 - dyp
显示剩余2条评论
2个回答

2
如果我要猜的话,成员函数会在初始化“this”时从“A *”转换为“A const *”,而非成员函数将“A const&”引用绑定到非const对象上,这实际上并不是一种转换,而只是在重载决议过程中的一个不优先的情况。当参数类型与相应的参数类型之间的路径涉及不同类型的转换时,就会出现该消息。标准拒绝将苹果与橘子进行比较,例如指针限定与引用绑定或整数提升与转换运算符,但GCC愿意。

1
成员函数的隐式对象参数应该是(应该是)引用类型[over.match.funcs]/4,因此在这两种情况下,我们都应该有一个A const&参数。 - dyp
@dyp GCC 中发生的事情是 GCC 自己的事情。这只是一种事后的解释。 - Potatoswatter
啊,好的。这就是为什么我插入了一个“应该是” ;) 所以你猜测gcc使用指针类型作为隐式对象参数?(那将是不符合规范的..) - dyp
2
@dyp 重载实现涉及许多特殊情况,因此指针可能会在某个地方弹出。只要所有格式正确的情况都被正确解释,而格式不正确的情况都至少产生一个警告消息,标准就不会关心。但是,这只是我的假设,我不打算深入挖掘。 - Potatoswatter

1
在C++中,您可以将方法(和运算符)分为const非const。当您不这样做时,编译器会搜索"最佳匹配"。对于非const的最佳匹配是const。请看以下示例:
struct S{
  void f(){cout<<"f";}
  void f()const{cout<<"f-const";}
};

int main(){
   const S sc;
   S s;
   s.f();
   sc.f();
   return 0;
}

第一个print的输出结果将是"f",但第二个print的输出结果将是"f-const"

如果我从结构体中删除"not const"方法,则两个对象的输出结果将相同。这是因为函数被称为"最佳匹配",因为它可以将"constiness"添加到非常量对象中。(不能删除const方法,因为它根本不适合...)

在您的代码中,没有显式的"not const"运算符,因此当它寻找"最佳匹配"时,它可以从选项中选择,并选择看起来最好的选项。您收到了警告,因为有2个匹配,但仍然选择了其中之一,我不知道为什么一个看起来比另一个更好...但对于"const",它有两个显式函数,编译器不能在具有显式方法时进行选择!这就是为什么您会收到错误消息的原因。

如果您希望获得相同的行为,请添加"explicit not const"运算符,如下所示:

struct A {
    void operator++() const {}
    void operator++(){};
};

void operator++(const A&) {}
void operator++(A&) {}

现在你会为两者都收到相同的错误。

2
谢谢您抽出时间写这篇文章。但是我的问题真正想问的是:非成员函数与成员函数相比,它为什么更适合? - bap
为什么不行呢?对我来说它们看起来是相同的。也许是因为前缀操作符更难将操作符分配给结构体? 关键不在于为什么选择其中一个而不选择另一个。如果您让它自己选择,它会自由决定选择什么,但可能其他编译器会选择成员运算符,因为C++标准在这种情况下不太清楚,因此您会得到警告。 - SHR
根据警告,标准是清晰的。但 OP 想知道实现者是如何得出这个结论的。你并没有回答那个问题。 - pmr
两种转换对编译器来说看起来是一样的。当它们是隐式的并且两个转换是相同的时候,它会选择其中一个(也许是全局的?我很确定如果你从结构上下文中调用它,它会选择成员的那个),并给出一个警告。当它是显式的时候,它必须引发一个错误,就像在const情况下发生的那样。 - SHR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接