2

A C++ guru told that varying the function parameter type with const in derived class, will break the virtual call mechanism.

I tried a simple program ( forgive for non standard code, written purely for test) , which prove otherwise. The function parameter change by const value will not break the virtual mechanism,

Is there any reasons & documentation pointing to this behavior?

Behavior noted with VS 2012 compiler & latest g++ compiler.

#include <iostream>
using namespace std;


class Base
{
public:
Base(){ cout<<"base"<<endl;}
virtual ~Base(){ cout<<"dest base"<<endl;}
virtual void test(const int x){ cout << "base test"<<"x = " << x<<endl;}
};

class Derived : public Base
{
public:
Derived(){ cout<<"derived"<<endl;}
virtual ~Derived(){ cout<<"dest derived"<<endl;}
virtual void test(int x){ cout << "derived test"<<"x = " << x<<endl;}
};
int main() {

    Base *b = new Derived();
    b->test(10);
    delete b;
    return 0;
}

output:

base
derived
derived testx = 10
dest derived
dest base
buddy
  • 805
  • 1
  • 15
  • 30
  • 2
    Your guru was probable talking about something like `foo(int&)` vs `foo(const int&)`. –  Apr 18 '18 at 18:35
  • 4
    Beware that, in general, code that demonstrates a behavior is not proof of that behavior. For example, I could construct an example on my platform that uses a dangling reference to an `int` and that would appear to work. But that isn't proof that it works in general, only that I was (un)lucky with my undefined behavior. Because of the existence of undefined behavior, showing that something behaves in a certain way doesn't prove that it will always behave that way. It's best to instead rely on credible documentation, ideally the standard itself. – François Andrieux Apr 18 '18 at 18:40
  • 3
    likely dupe of [Top-level const doesn't influence a function signature](https://stackoverflow.com/questions/17208570/top-level-const-doesnt-influence-a-function-signature) or [Why does a function declaration with a const argument allow calling of a function with a non-const argument?](https://stackoverflow.com/questions/3681188/why-does-a-function-declaration-with-a-const-argument-allow-calling-of-a-functio); see also the more searchable [const qualifier disappears from pure virtual function](https://stackoverflow.com/questions/24290732/const-qualifier-disappears-from-pure-virtual-function) – underscore_d Apr 18 '18 at 18:46
  • 1
    It doesn't "break" anything; it's simply that an overriding function must have the same argument list as the function that it overrides. Just as `void f(int)` in a derived class doesn't override `virtual void f(double)` in a base class, `void f(int&)` doesn't override `virtual void(const int&)`. – Pete Becker Apr 18 '18 at 19:03

5 Answers5

6

The top level cv-qualifier is not part of function signature, they are simply ignored.

[dcl.fct]/5

After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.

llllllllll
  • 16,169
  • 4
  • 31
  • 54
4

Your C++ guru is wrong (provided you understood them, gurus tend to talk in cryptic messages). Const-qualifier on the argument type itself is not a part of a function signature at all.

For example, void foo(int* const ); is no different from void foo(int* ). Please note, it is not the same as const qualification of the indirect object, like void foo(const int* ) is different from void foo(int* ).

In your particular case, void test(int x) is the same as void test(int const x)

SergeyA
  • 61,605
  • 5
  • 78
  • 137
4

void test(int) != void test(int) const and would "break" virtual call.
and void test(int&) != void test(const int&) and would "break" virtual call.

void test(int) is the same declaration than void test(const int) and won't "break" virtual call.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
3

The equivalent of std::decay happens to argument types for functions. (It's actually the reverse, std::decay is modeled after what functions arguments do.)

The outermost const will be dropped from the signature. By outermost, think of types as envelopes composed over different types. A pointer to const int is a different type than a pointer to int, and will result in a different function signature. (With a pointer, you can think of the pointer itself as the outer thing, and what it points to is "inner" and not modified.)

  • const int - becomes just int
  • int * is unchanged and remains int *
  • const int * is unchanged and remains const int * - const is on the int, not the pointer, and only the outermost const is dropped
  • int const * is unchanged and remains int const * - const is on the int, not the pointer, and only the outermost const is dropped. (Note, this is 100% identical in meaning as const int *)

  • int * const - changes to int * - because const qualifies the outermost pointer

  • int * const * const becomes int * const * - outer const is dropped on outer pointer, inner const is not dropped on the inner pointer.
  • const int * const * const becomes const int * const * - outer const is dropped on outer pointer, inner const is not dropped on the inner pointer, and const is also kept on the internal-most int
  • MyTemplate<const T> - unchanged, remains MyTemplate<const T>, because the const isn't on the outer type, but nestled in a template parameter

So yes, const does affect type, but not in the simple case like you tried. Only when it's contained inside of a type, not affecting the outermost type.

If you read types from right-to-left it can help. If the rightmost thing in a type specifier is a const, it is always dropped (example int * const). If the leftmost thing is const, it is only dropped if the thing it is qualifying is the rightmost thing in the type (example const int, the leftmost thing is const and it affects the int to its right AND the int to its right is the rightmost thing in the type.) (example 2: const * int not dropped, because leftmost const modifies the thing to its right that is not the rightmost thing in the type.)

Chris Uzdavinis
  • 6,022
  • 9
  • 16
  • 1
    This is a good perspective on it. Also: This is why I strictly enforce right-to-left types in all my own code! `int const *const` it has to be. – underscore_d Apr 18 '18 at 18:58
  • When I get to decide the coding style, that's my rule too. Yes, the rule for const is to modify the thing to its left _unless_ there is nothing to its left, in which case it does the thing to its right. Modifying the thing to its right is the _exception_ to the rule, and makes reading out loud right-to-left more cumbersome. – Chris Uzdavinis Apr 18 '18 at 19:30
1

He was right.just found it by seeing the warning.

In this case : Compilers before VS2008 would break the virtual mechanism in this case.

Later compilers it gives warning C4373: virtual function overrides '%$pS', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers

found the documentation here https://msdn.microsoft.com/en-us/library/bb384874.aspx

buddy
  • 805
  • 1
  • 15
  • 30
  • 2
    _"He was right"_ does not follow from _'one compiler was buggy at one point in time'_ (and now emits a rather spurious warning). This is a useful factoid, for sure. However, it's not an answer. A "C++ guru" would refer to the Standard, not one version of one compiler. – underscore_d Apr 18 '18 at 18:47
  • 1
    @underscore_d if this compiler is actually used in this setting, than of course guru is right, but the OP is wrong omitting this detail. – SergeyA Apr 18 '18 at 18:49
  • Added up there, thanks for giving loads of good answers! – buddy Apr 18 '18 at 18:55
  • Writing portable code, you still have to think about versions? – buddy Apr 18 '18 at 19:09
  • 1
    It's not _right_ if it's an untrue statement--even if it happened to function as expected in one specific, broken context. At best, it's just not "100% wrong in all situations." – Chris Uzdavinis Apr 18 '18 at 20:03