Name resolution and overloading.

In C++ we’re allowed to use the same name for functions that take different parameter types and/or of different arity. For example:

void f();
void f(int);
void f(std::string);

The three versions of `f` above can coexist in the same program, within the same namespace or class, and don’t break the one-definition rule. They are different functions. This fact can often lead us to begin assuming that parameters are part of the function name. However, in C++ this is not the case and this assumption can often lead us to write code that does things we do not expect.

The truth of the matter is that all of the `f` functions have the same name: f. In most cases this detail can be ignored but sometimes it becomes very important. Consider for example this code:

struct type0 {};
struct type1 {};

struct base0 { virtual int get(type0) const = 0; };
struct base1 { virtual double get(type1) const = 0; };

struct test_object_ : base0,base1 {};
struct test_object : test_object_
{
  int get(type0) const { return 5; }
  double get(type1) const { return 42.666; }
};

int main()
{
  test_object o;
  test_object_ * op = &o;

  int x = o.get(type0());  // 1
  int y = op->get(type0()); // 2
} 

Can we expect that code to compile? The answer actually is no, it will not. The reason this code will not compile is that name resolution happens before overload resolution, and the scope of a name can make a huge difference as to whether overload resolution even applies. To explain a bit about this, lets take a look at the line labeled ‘1’:

int x = o.get(type0());

In this case, the name `get` is looked up within the scope of test_object, a class. This means that member name resolution applies and because test_object declares one or more `get` names, the compiler stops there. This is called “hiding” and means that no `get` name within bases is never searched for. Now that the name `get` has been resolved to two possible functions within the same class declaration those two functions are candidates for parameter overload resolution. Since only one version of `get` actually works with the `type0` type, that version of `get` is called.

Next let’s consider the line labeled ‘2’:

int x = op->get(type0());

This case is quite different even though it seems as though it should be exactly the same. In this case the static type of the class being used is not `test_object` but instead `test_object_`, its base. This class doesn’t declare any new names but gets all of its names from base classes. This being the case, `get` is not declared in `test_object_` and so the name resolution rules begin applying for base classes and it is here that things go wrong. Because `base0` and `base1` are unrelated, the name `get` is retrieved from both of these classes. One would think that this set of possible functions would be considered for overload resolution but the standard specifically says that they are not in 10.2/2:

If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

All of this happens before overload resolution and even virtual function lookup! Thus the compiler never eliminates `get(type1)` from the list before barfing an error.

The trouble doesn’t actually end there either. The rules for overload resolution are quite complicated and can work in ways that even moderately advanced developers don’t expect: Expected conversion routine to be called

Advertisements

One Response to “Name resolution and overloading.”

  1. This rule does not seem to apply for operator() and therefore for lambdas.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: