I'm using Clang-Tidy to inspect my code. One of the tool's messages puzzled me:
clang-tidy: Default arguments on virtual or override methods are prohibited
I get the idea when I actually use a method override. But here in my case, I want to finalize the use of the method. So I put the "final" keyword at the end of my method signature to prevent it from overriding. And to do this, we must use "virtual" keyword as well.
I don't understand why I could't use default arguments there. If I remove the "virtual/final" attributes, Clang-Tidy leave me alone, but permits to shadow the method by accident, and I don't want that.
So, is this a false-positive ? A bug ? Or a real problem I should take care of ?
Here is the minimum reproducible example :
The case I want to avoid :
#include <iostream>
class A
{
public:
A () = default;
virtual ~A () = default;
virtual void overrideableMethod () {
std::cout << "Method from A !" << std::endl;
};
void nonOverrideableMethodEvenByAccident (int a = 0) {
std::cout << "Method from A ! V:" << a << std::endl;
}
};
class B : public A
{
public:
B () : A() {}
void overrideableMethod () override { // --> Fine.
std::cout << "Method from B !" << std::endl;
};
void nonOverrideableMethodEvenByAccident (int a) { // -> Must be an error. (Omitting override here do the trick, so it's weak !)
std::cout << "Method from B ! V:" << a << std::endl;
};
};
int main(int argc, char const *argv[])
{
B b{};
b.overrideableMethod();
b.nonOverrideableMethodEvenByAccident(9);
/* output :
Method from B !
Method from B ! V:9
*/
return 0;
}
The case I want (A compiler error), but without the clang-tidy warning :
#include <iostream>
class A
{
public:
A () = default;
virtual ~A () = default;
virtual void overrideableMethod () {
std::cout << "Method from A !" << std::endl;
};
virtual void nonOverrideableMethodEvenByAccident (int a = 0) final { // [WARNING] clang-tidy: Default arguments on virtual or override methods are prohibited
std::cout << "Method from A ! V:" << a << std::endl;
}
};
class B : public A
{
public:
B () : A() {}
void overrideableMethod () override { // --> Fine.
std::cout << "Method from B !" << std::endl;
};
void nonOverrideableMethodEvenByAccident (int a) { // [ERROR] error: virtual function ‘virtual void B::nonOverrideableMethodEvenByAccident(int)’ overriding final function (with or without override, great !)
std::cout << "Method from B ! V:" << a << std::endl;
};
};
int main(int argc, char const *argv[])
{
B b{};
b.overrideableMethod();
b.nonOverrideableMethodEvenByAccident(9);
return 0;
}
I don't understand why I could't use default arguments there.
Well, you can, but it is against the Google C++ Style Guide. The reasoning is explained in the documentation, see Default Arguments.
If you don't care about this rule, you can disable the google-default-arguments check (using --checks=-google-default-arguments
).
Another solution is to use an overloaded function without arguments instead of a function with default arguments. This does not result in the mentioned Clang-Tidy warning, but still does result in a compiler error when (accidentally) overriding nonOverrideableMethodEvenByAccident()
, as you want:
#include <iostream>
class A
{
public:
A () = default;
virtual ~A () = default;
virtual void overrideableMethod () {
std::cout << "Method from A !" << std::endl;
};
virtual void nonOverrideableMethodEvenByAccident (int a) final { // No default argument!
std::cout << "Method from A ! V:" << a << std::endl;
}
virtual void nonOverrideableMethodEvenByAccident (void) final { // Function without arguments
nonOverrideableMethodEvenByAccident(0);
}
};
class B : public A
{
public:
B () : A() {}
void overrideableMethod () override {
std::cout << "Method from B !" << std::endl;
};
// void nonOverrideableMethodEvenByAccident (int a) { // error: virtual function ‘virtual void B::nonOverrideableMethodEvenByAccident(int)’ overriding final function
// std::cout << "Method from B ! V:" << a << std::endl;
// };
};
int main(int argc, char const *argv[])
{
B b{};
b.overrideableMethod();
b.nonOverrideableMethodEvenByAccident(9);
b.nonOverrideableMethodEvenByAccident(0);
return 0;
}
Output:
Method from B !
Method from A ! V:9
Method from A ! V:0