Overriding Virtual Functions? Use C++0x Attributes to Avoid Bugs.

Overriding Virtual Functions? Use C++0x Attributes to Avoid Bugs.

n July 2009, the C++ standards committee voted into the C++0x Working Paper new attribute tokens that regulate the overriding of virtual functions in a derived class. These new attributes ensure that an overriding function matches its base class counterpart by signature and name.

As trivial as it may seem, overriding a virtual member function can go wrong in many ways, most of which go undetected by the compiler. This 10-Minute Solution shows how to use the new C++0x attributes to avoid a few common bugs associated with overriding virtual functions.

While overriding a virtual function, you accidentally misspell the function’s name or inadvertently changed its parameter list. The compiler doesn’t catch these bugs.

Add the [[base_check]], [[hiding]], and [[override]] attributes to your derived classes. This will help the compiler catch errors that are otherwise undetectable.

Misspelled, Missed Error

Suppose you have a virtual function in a base class B and a derived class D that overrides that function:

struct B { virtual void func();};struct B: D { void func();};

C++ programmers assume that D::func() overrides B::func deliberately, but what if the programmer accidentally picks the name func() without noticing that D::func() overrides B::func()? The compiler can’t catch such accidental misnaming errors because the code is perfectly valid, even though it means something different from what the programmer intended.

Inadvertent overriding is fairly common. It occurs, for example, in projects where class libraries that contain hundreds of virtual functions are used. Two other problems occur even more frequently in class hierarchies:

  • Misspelled function names
  • Parameter mismatches

Let’s look at these two more closely.

Suppose you’re using a library that includes the following class:

struct Base { virtual void some_func1(); virtual void some_func2() const; virtual void some_func3(long);};

You want to derive a new class that will override the three member functions of Base. Unfortunately, you end up with code that does something quite different:

struct Derived: Base { void sone_func1(); //misspelled name, doesn't override void some_func2(); //cv-mismatch, hides Base::some_func2 void some_func3(long long); //parameter mismatch, hides Base::some_func3};

Notice that none of the three member functions of Derived actually overrides the corresponding member function of Base! What went wrong?

The function sone_func1() is misspelled. Instead of overriding Base::some_func1(), the compiler assumes that the programmer simply added a new function to Derived. So, some_func2() hides?rather than overrides?Base::some_func2(), because the two functions have incompatible cv-qualification.

Finally, Derived::some_func3() also hides Base::some_func3() because the parameter lists of the two functions don’t match.

Recall that implicit hiding is valid in C++. Some compilers issue a warning when a member function hides a base class name, while others don’t. The declaration of Derived thus compiles successfully, which makes these bugs much harder to detect, especially when an unsuspecting client uses code written by a third party.

Using Attributes

The compiler can’t guess the programmer’s intent. That is why misspelled function names and accidental hiding of base class functions go undetected. Three new C++0x attributes tackle this problem:

  • [[base_check]]: You use this attribute in a declaration of a derived class. It indicates that any name hiding or virtual function overriding within that class shall be marked with the appropriate attribute. Otherwise, a compilation error will occur.
  • [[override]]: This attribute applies to an overriding member function in a derived class. It indicates that a member function overrides a virtual function in a base class. If no appropriate virtual function is found, the program is ill formed.
  • [[hiding]]: This attribute indicates a member of a derived class that deliberately hides a member with the same name in a base class.

To “activate” the attributes [[override]] and [[hiding]], you first need to declare a derived class with the [[base_check]] attribute. Without [[base_check]], a derived class behaves as usual, allowing you to hide and override base class functions implicitly. A modified version of Derived looks like this:

struct Derived [[base_check]]: Base { void sone_func1 [[override]](); //error, spelling void some_func2 [[override]](); //error, signature void some_func3 [[override]](long long); //ditto};

Because the members of Derived are all declared [[override]], the compiler tries to match each one of them with a corresponding virtual function in the base class but fails. That leads to explicit compilation errors that tell the programmer exactly what’s wrong with the code.

Hide and Seek

You can add new functions to a derived class as well. A new function is indicated by the lack of any attributes:

struct Derived [[base_check]]: Base { int new_func(); //OK//...};

In some cases, hiding a name in a derived class makes sense. For that purpose, you have to use the [[hiding]] attribute:

struct Derived [[base_check]]: Base { void some_func1[[hiding]](double);//OK  void some_func2(short); //error, implicit hiding//...};

Implicit hiding in the class Derived isn’t allowed, because that class is declared [[base_check]]. Omitting [[base_check]] will switch off the compiler’s safeguards, reversing the validity of the functions’ declarations:

struct Derived : Base { void some_func1[[hiding]](double);//error,[[base_check]] required void some_func2(short); //OK, implicit hiding //...};

Attributes Are Your Friends

The attributes proposal drew criticism because some believed it was conceived as a mechanism for sneaking keywords into C++ and because many people don’t like the [[]] syntax. However, attributes are here to stay. They seem to solve a few annoying problems that previously had proved resistant to compilers.


About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist