#include <iostream>
using namespace std;
#include "other_library.h"
struct Foo
{
Foo() { cout << "class\n"; }
};
int main()
{
Foo(); // or Foo() in any expression
}
This outputs class
, or so we would think. The problem is that if other_library.h
happens to have a function called Foo
whose return type is suitable to appear in whatever expression we used Foo
in then it silently changes the behaviour, e.g.:
int Foo() { cout << "func\n"; return 1; }
causes func
to be output without any code changing in main
. This is bad because of the possibility for insidious and hard-to-detect bugs ; even if it is not malicious intent on the part of other_library
, a name clash could go undetected.
What is a good way to deal with this problem? It was originally raised by Dan Saks in 1997, and one suggested resolution is that all classes should be typedef'd:
typedef struct Foo Foo;
as the compiler does have to report a clash between a typedef-name and a function name. However this does not appear to be common practice - why not?
Clarification: this question is about good practices for our code to stop this undetected behaviour change happening without us noticing. (As opposed to how to solve it once we have realized that it is happening, which is easier -- e.g. rename our class).
typedef struct Foo Foo;
as the compiler does have to report a clash between a typedef-name and a function name. However this does not appear to be common practice - why not?
I hold this truth to be self-evident: cumbersome code is cumbersome. You can actually rather easily fix this by working in a namespace of your own (which, assuming the header file is C++, too, the other_library
should also do.
Unfortunately, you cannot avoid this. From C++11 standard (3.3.10):
A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.
The only way to guard against it is the typedef trick you pointed out (thanks for that!), using namespaces, or adhering to naming conventions (the latter not helping when dealing with 3rd party code). You could also try to wrap the include in a namespace (shown bellow), but as the comments pointed out, this could cause link errors in certain situations.
namespace otherlib {
#include "other_library.h"
}
Then:
otherlib::Foo();
current best practises protect against this already
Naming Conventions, such asvoid camelCase()
vs class PascalCase
namespaces
wrapper libs for everything not modern C++
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With