Недавно я видел этот стандартный абзац C++ (http://eel.is/c++draft/expr.post#expr.call-5):
If the postfix-expression designates a destructor, the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or cv void.
Я не совсем понимаю эту часть:
the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different.
even if the type of the function actually called is different.
Как выражение вызова может на самом деле вызывать функцию другого выбранного типа?





виртуальные функции могут иметь ковариантный тип возврата,
так с
struct Base
{
virtual ~Base() = default;
virtual Base* Clone() const { return new Base(*this); }
};
struct Derived : Base
{
// covariant return type:
Derived* Clone() const override { return new Derived(*this); }
};
потом
Derived d;
Base& b = d;
auto* clonePtr = b.Clone(); // `auto` is `Base`, even if `Derived::Clone` is called.
// runtime type of `clonePtr` is `Derived`
std::unique_ptr<Base> clone(clonePtr); // Done in 2 steps for explanation
Во-первых, пример для иллюстрации.
struct B {
virtual B* f() { return this; }
};
struct D : B {
D* f() override { return this; }
};
void bar(B*) {}
void bar(D*) {}
int main() {
D d;
B& b = d;
bar(b.f()); // calls `bar(B*)`
}
Здесь постфиксное выражение b.f обозначает функцию. Это B::f, и его тип возврата - B*. Даже если при переопределении f указанный тип возвращаемого значения является ковариантным (D*). Тот факт, что фактический вызов (предположительно) разрешен во время выполнения, не меняет того факта, что мы статически выбираем личность функции. Это уместно, когда есть перегрузка. Одно и то же имя функции может обозначать две или более функций, и это разрешение перегрузки, которое (статически) выбирает, какую перегрузку вызывать. Эта перегрузка может быть переопределена в производном классе, но опять же ее идентификатор статичен.
Я выбрал этот как принятый, потому что он действительно помогает понять, что означает statically chosen function. Спасибо.
Неуклюжая формулировка, напоминающая читателю, что поиск имени, разрешение перегрузки и определение типов выражений выполняются во время компиляции, поскольку C++ является языком статического типа. Нет необходимости напоминать читателю о std, поскольку это предполагается практически повсеместно. Очевидно, что поиск имени не может быть выполнен в динамическом типе вычисляемого выражения. Контроль доступа также учитывает статические типы. Шаблоны также не создаются во время выполнения!