为什么我们允许获取不完整类型的地址?

10

考虑以下代码:

class Addressable;
class Class1  { void foo(Addressable &a) { (void) &a; } };  // OK
class Addressable { void *operator &() { return this; } };
class Class2  { void foo(Addressable &a) { (void) &a; } };  // Error: operator & private

C++为什么允许获取不完整引用类型的地址?

这可能是非法的,如上所示。这是有意为之吗?


4
我认为更好的问题是,“为什么C++首先允许对&进行重载”:D - Antti Haapala -- Слава Україні
我从不知道你可以重载 operator &()。有时候我觉得 C++ 给了开发者太多的自由。 - tenfour
@tenfour:我只能想到一种情况有用,那就是在lambda符号中表示取地址操作,例如foo[&_1] - user541686
如果有人知道一个好的理由,那么应该记录下来有哪些合法的原因可以重载一元运算符&?,因为现在的答案看起来像是“嗯,不知道”。 - Antti Haapala -- Слава Україні
@AnttiHaapala:已添加! - user541686
1个回答

5

是的,这是有意为之的,如果重载了operator&,那么可能会导致程序出现故障。

在C++很久以前就可以使用不完整类型的地址。在C语言中,绝对不会出现任何故障风险,因为&无法重载。

C++选择不必要地破坏先前有效的程序,并简单地规定,如果不完整类型确实有重载的&运算符,则未指定是否使用重载运算符。

引用N4140:

5.3.1 一元运算符[expr.unary.op]

如果将&应用于不完整类类型的左值,并且完整类型声明了operator&(),则未指定是使用内置含义还是调用运算符函数。

这可以解释为适用于当前正在声明的类,甚至在已经看到operator&的声明时也是如此:

extern struct A a;
struct A {
  int operator&();
  decltype(&a) m; // int, or A *?
};
int main() {
  return A().m; // only valid if m is int
}

在这里,GCC将m类型定义为A *并拒绝该程序,但是clang将其定义为int类型并接受它。


+1,哇。即使类型不完整时没有使用&,最后一句话是否仍然成立?换句话说,编译器是否可以始终明显忽略重载的& - user541686
@Mehrdad 我不明白你的评论是什么意思。如果编译器发现类型不完整,那么尝试查找重载的 operator& 将会失败,因此只能找到内置的运算符。但同时,编译器不需要精确地跟踪程序中类型尚未完全的具体位置,如果编译不是严格从上到下进行的,即使您不希望它这样做,它也可能将类型视为完整(并找到重载运算符)。 - user743382
我只是想说你最后一句话没有明确说明行为何时未指定:它总是未指定的,还是只有在&未定义时才未指定? - user541686
@Mehrdad 没问题。我编辑了我的答案,给出了一个具体的例子。 - user743382

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