为什么这句话"The expression can be used only as the left-hand operand of a member function call"在 [expr.re



[expr.ref]p(6.3.2):

否则,如果E1.E2引用非静态成员函数和E2的类型是"参数类型列表cvref 限定符opt返回 T 的函数",则E1.E2是一个 prvalue。该表达式指定一个非静态成员函数。这 表达式只能用作成员的左侧操作数 函数调用 ([class.mfct])。[ 注意:任何冗余集 表达式周围的括号将被忽略 ([expr.prim.paren])。 — 尾注 ]E1.E2的类型是"参数类型列表函数"简历返回T"。

例如,下面main中的第二条语句无法编译,可能是因为上面突出显示的句子。但是,为什么语言设置为以这种方式工作呢?

#include<iostream>
void g();
struct S { void f(); };
S s;
int main(){
std::cout << "decltype(g) == void() ? " << std::is_same<decltype(g), void()>::value << 'n';            // Ok
std::cout << "decltype(s.f) == void() ? " << std::is_same<decltype(s.f), void()>::value << 'n';        // Doesn't compile probably because of the sentence hihlighted above in [expr.ref]p(6.3.2).
}

当你做E1.E2时,你不是在谈论E1类型的一般性质。您请求访问E1指定的对象中的事物,其中要访问的事物的名称为E2。如果E2是静态的,它访问类静态的东西;如果E2是非静态的,则它访问特定于该对象的成员事物。这很重要。

成员变量成为子对象。如果您的类S具有非静态数据成员int i;,则s.i是对int的引用。从int&的角度来看,这种引用的行为与任何其他int&没有什么不同。

让我更清楚地说:任何int*int&都可以指向/引用一个完整的对象或作为其他对象的子对象的intint。单结构int&可以通过这种方式承担双重任务。*

鉴于对s.i的理解,s.f的假定含义是什么?嗯,它应该是相似的,对吧?s.f将是某种东西,当用params调用时,将等同于做s.f(params)

但这不是C++中存在的东西。

C++中没有任何语言结构可以表示s.f的含义。这样的构造需要存储对s以及成员S::f的引用。

函数指针无法执行此操作。函数指针需要能够与void***进行指针互转换。但是这样的s.f需要存储成员S::f以及对s本身的引用。所以根据定义,它必须大于void*

成员指针也无法执行此操作。成员指针显式不随身携带其this对象(这就是重点);您必须在调用时使用特定成员指针调用语法.*.->提供它们。

哦,有一些方法可以在语言中对此进行编码:lambdas,std::bind等。但是没有语言级别的结构具有这种精确的含义。

因为C++以这种方式是不对称的,s.i具有可编码的含义但不具有s.f,C++使不可编码的含义成为非法的。

你可能会问为什么这样的结构不能简单地构建。其实没那么重要。该语言按原样运行良好,并且由于s.f需要的复杂性,最好让你使用 lambda(诚然,应该有办法让它更短来编写这样的东西)如果这是你想要的。

如果你想让裸s.f等同于S::f(即:指定成员函数),那也不起作用。首先,S::f也没有类型;使用这样的 PRvalue 您唯一能做的就是将其转换为指向成员的指针。其次,成员函数指针不知道它来自哪个对象,所以为了使用一个来调用成员,你需要给它s。因此,在调用表达式中,s必须出现两次。这真的很愚蠢。

*:您可以执行某些操作来完成无法对子对象执行的操作。但是这些会引发UB,因为它们无法被编译器检测到,因为int*不会说它是否来自子对象。这是重点;没有人能分辨出其中的区别。

**:标准不要求这样做,但标准不能做一些完全使这种实现无法的事情。大多数实现都提供此功能,基本上任何 DLL/SO 加载代码都依赖于它。哦,它也与 C 完全不兼容,这使得它无法启动。

stat_result.st_mtime语法继承自 C,在 C 中它总是有一个值(更具体地说,是一个左值)。 因此,即使在方法调用的情况下,它在语法上也是一个表达式,但它没有价值,因为它是与其关联的调用表达式一起计算的。

它(如您所引用的)给定成员函数的类型,以满足通过正确类型的表达式调用函数的要求。 但是,为它定义decltype误导,因为它不能是一个完整的表达式(就像每个未计算的操作数一样),并且具有decltype的常见表达式 SFINAE 不会阻止受保护实例化的硬错误。

请注意,非静态成员函数可以在未计算的操作数中命名,但这允许S::f(或仅在类中f,尽管可以说被重写为在成员函数中(*this).f),而不是s.f。 该表达式具有相同的类型,但出于同样的原因,该表达式本身受到限制(与&一起出现以形成指向成员 [function] 的指针):如果要以其他方式使用它,它将可用作普通的 [function] 指针,这是不可能的。

相关内容

最新更新