Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[class.virtual] Overriding virtual function through an explicit object member function CWG2554 #5144

Open
xmh0511 opened this issue Dec 10, 2021 · 16 comments · May be fixed by #5145
Open

[class.virtual] Overriding virtual function through an explicit object member function CWG2554 #5144

xmh0511 opened this issue Dec 10, 2021 · 16 comments · May be fixed by #5145
Assignees
Labels
cwg Issue must be reviewed by CWG. not-editorial Issue is not deemed editorial; the editorial issue is kept open for tracking.

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented Dec 10, 2021

Consider this example

struct Base{
   virtual void show(){}  // #1
};
struct Derived: Base{
    void show(this Base bptr){}  // #2
};

According to [basic.scope.scope] p1, #1 corresponds to #2 as per [basic.scope.scope] p4 if their object parameters should be corresponding. [basic.scope.scope] p3 says that

Two non-static member functions have corresponding object parameters if:

  • exactly one is an implicit object member function with no ref-qualifier and the types of their object parameters ([dcl.fct]), after removing top-level references, are the same, or
  • their object parameters have the same type.

In this case, #1 and #2 satisfy the first bullet since the type of the implicit object parameter of #1 is Base& while the type of the explicit object parameter of #2 is Base, they have the same type after removing the top-level references. Hence, #2 can override#1? Is it the intention after introducing deducing this?


If an explicit object member function can override an implicit object member function, it seems that the class type of the explicit object parameter must be the base class. Is it also the artificial intent?

@xmh0511 xmh0511 changed the title [class.virtual] Overriding virtual fuction through an explicit object member function [class.virtual] Overriding virtual function through an explicit object member function Dec 10, 2021
@jensmaurer
Copy link
Member

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html clearly says this about the intent:

struct B3 {
    virtual void f();
};

struct D3 : B3 {
    void f(this D3&); // ok, does not override B3::f
};

So, it was originally intended that old-style and new-style member functions don't override each other.
However, since explicit/implicit member functions can now be used for redeclarations (not just overrides), this seems not obviously relevant anymore.

It seems to me the problem is that explicit-object member functions with a by-value parameter correspond to an implicit-object member function (which has a by-reference parameter). This may have calling convention impact, so is probably not desired. Example:

struct C {
  void f();    // #1
};
void C::f(this C) { } // ok, refers to #1

I think this should not be a redeclaration.

@brevzin , @opensdh , any thoughts?

@jensmaurer
Copy link
Member

I think my C::f example is misguided; those declarations correspond, but declare different entities, so this is ill-formed.

I think we need a narrow fix for [class.virtual] that says that a virtual member function shall not be an explicit-object member function (or that only same-kind functions override). Or something like that.

@brevzin
Copy link
Contributor

brevzin commented Dec 10, 2021

We say they cannot be declared virtual here: http://eel.is/c++draft/dcl.dcl#dcl.fct-6

@xmh0511
Copy link
Contributor Author

xmh0511 commented Dec 10, 2021

@brevzin Derived::show isn't declared virtual since the declaration contains no virtual specifier. be declared virtual is vague on its meaning that whether it is a virtual function or has a virtual specifier on its declaration?

@jensmaurer
Copy link
Member

@brevzin , unforunately, that's not quite sufficient, because "declared virtual" strongly hints that the keyword "virtual" actually appears on the declaration. With overriding, a function may be virtual without being so declared.

I think we need "An explicit object member function shall not be virtual." in [class.virtual], then we can drop the "virtual" keyword prohibition in [dcl.fct] p6 and possibly replace it with a note.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Dec 10, 2021

@jensmaurer Hmmm, I would argue that void C::f(this C) and void C::f() declare the same entity since they obey [basic.link] p8, I think the reason why they are ill-formed is stated by [basic.link] p11, that is, they declared the same entity but they have the different function type.

@jensmaurer
Copy link
Member

Tangent: "corresponds" is not the right criterion to define "override": http://lists.isocpp.org/core/2021/12/11840.php

@jensmaurer jensmaurer added cwg Issue must be reviewed by CWG. not-editorial Issue is not deemed editorial; the editorial issue is kept open for tracking. labels Mar 23, 2022
@jensmaurer
Copy link
Member

CWG2554

@jensmaurer jensmaurer changed the title [class.virtual] Overriding virtual function through an explicit object member function [class.virtual] Overriding virtual function through an explicit object member function CWG2554 Mar 24, 2022
@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 24, 2022

CWG2554

What is the purpose of "ignoring object parameters" in the proposed suggestion? Does it intend to say that just skip [basic.scope.scope] p3 when we check whether such two non-static member functions correspond where there is at least one explicit object member function? However, even if we ignore the object parameters(i.e. the implicit and explicit object parameters), they still are non-static member functions, which means

if both are non-static members, they have corresponding object parameters

this rule still can work. If the wording means this intent, Is it better to say

if at least one is an explicit object member function, ignoring object parameters they are considered to have corresponding object parameters.

@jensmaurer
Copy link
Member

Yes, and we ignore any non-corrrespondence in object parameters, so "if both are non-static members, they have corresponding object parameters" is essentially void.

I agree it would be better to phrase the rule in a more constructive rather than "do this, except X" way.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 24, 2022

I agree it would be better to phrase the rule in a more constructive rather than "do this, except X" way.

Totally agree. Unlike ignoring trailing requires-clauses, that can mean we ignore any difference from trailing requires-clauses when considering whether two declarations correspond. The special here is even though we ignore object parameters, it cannot change the reality that they are non-static member functions. This will make "if both are non-static members, they have corresponding object parameters" hard to interpret when we consider it.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Apr 12, 2022

@jensmaurer A friendly reminder, CWG2554 seems to admit that these functions are all overriding in the following cases:

struct B3 {
    virtual void f();
};

struct D3 : B3 {
    void f(this D3&); // ok, does not override B3::f
};

This case is sourced from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html, the comment might be wrong since D3::f has an explicit object parameter and B3::f has an implicit object parameter, they correspond when we ignore the difference in their object parameters. So, D3::f does override B3::f.

Another case

struct A{
   virtual void show();
};
struct U{};
struct B{
   operator U();
   void show(this U){}
};

Again, B::show corresponds to A::show if ignore the difference in their object parameters, therefore, B::show overrides A::show even though their object parameters have no relationship at all.

@jensmaurer
Copy link
Member

Yes, discussion around CWG2554 has progressed beyond the intent originally expressed in P0847R7, including making more derivation examples ill-formed.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Apr 12, 2022

Ok. That means D3::f and B::show are final overriders in D3 and B, respectively, after CWG2554 revision? BTW, shall we modify CWG2554 to

If a virtual member function F is declared in a class B, and, in a class D derived (directly or indirectly) from B, there is a declaration of a member function G, G overrides F if:

  • if both are implicit object member functions, consider F to be a member of D for the purpose of defining the type of the implicit object parameter, G corresponds to F and they have the same ref-qualifier (or absence thereof)
  • if at least one is an explicit object member function, consider that G and F have corresponding object parameters(i.e. ignore their difference), G corresponds to F

In both cases, ignoring their trailing requires-clauses.

@jensmaurer
Copy link
Member

Yes, D3 and B override their base class functions and, with CWG2553, make the program ill-formed.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Apr 12, 2022

Thanks. Please consider the above suggestion for CWG2554.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cwg Issue must be reviewed by CWG. not-editorial Issue is not deemed editorial; the editorial issue is kept open for tracking.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants