Archive for March, 2011

Name resolution and overloading.

Posted in Uncategorized on March 28, 2011 by Crazy Eddie

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

Why I hate having to work with Visual Studio

Posted in Rant on March 10, 2011 by Crazy Eddie

The fact that the MS C++ compiler is one of the worse on the market is bad enough. Random crashes, lockups, and intellisense taking complete control while also providing next to no actual benefit…annoys me but I’m used to it. What really bothers me though is its tendency to enter the Twilight Zone.

Today reminded me of this fact yet again. For no apparent reason every editor area in the program (luckily only one instance) has begun to:

* Place the edit cursor wherever you put it whether or not there’s anything there (well past the end of lines in other words)
* Ignore arrows, backspace, delete, etc…
* Pop up some kind of window menu when I type ‘-‘.

This is the first time I’ve run into behavior this random, but I quite regularly run into a different kind of random craziness. Probably about 1-2 times a day one of the files I’m editing will randomly enter some strange mode where indentation doesn’t work and pressing ‘:’ puts you at the beginning of the line. Makes typing things out like “std::vector::iterator” more than a little annoying. Luckily simply closing the file and opening it again goes back into normal C++ mode from whatever insane mode it was previously in.