Archive for April, 2011

MSVC bug that’s constantly getting me

Posted in C++, cpp, Rant on April 20, 2011 by Crazy Eddie

This is partially a rant and partially instructional. I’ve just gotten bitten by this compiler bug, AGAIN, and I’m a little frustrated with it because it took a good 2 hours to find (involved template instantiations and the damn compiler never showed me the actual line of code trying to instantiate the template). As is the case with most MSVC bugs, this one involves templates.

Consider this code, what is the output you’d expect?

#include <iostream>

struct base
{
  template < typename T >
  base(T const&)
  {
    std::cout << "templated constructor\n";
  }

  base(base const&)
  {
    std::cout << "copy constructor\n";
  }
  base()
  {
    std::cout << "default constructor\n";
  }
};

struct derived
  : base
{
};

int main()
{
  derived d;
  derived d2(d);
}

If you expect the construction of ‘d’ to cause “default constructor” and the construction of ‘d2’ to output “copy constructor”, you’d be absolutely right to expect that. This is supposed to be how it works. A template constructor can never be a copy constructor and the copy constructor of a derived class is supposed to defer to the *copy* constructor of the base before building its own bits. Unfortunately, though you’re right to expect this behavior, you’d be absolutely wrong about it actually happening because MSVC is stupid here.

What you’re actually going to get from that code is a call to the template constructor in base due to the copying of a derived. The MSVC compiler simply passes the derived type up the chain. When it hits a template constructor it says, “Hey, I’ve got a static type of ‘derived’ here, which matches T better than it does ‘base const&’.” BAD, MSVC, BAD!

The workaround is to write a user-defined copy constructor and explicitly perform the cast that the compiler should be doing to begin with:

struct derived
  : base
{
  derived() : base() {} // need this now too.
  derived(derived const& other) 
    : base(static_cast<base const&>(other))
  {}
};

If you can keep this workaround in memory at all times then you’ll be safe. If you’re like me and don’t instantly think of every workaround you need to insert into your code every time you’re going to run into a well-known compiler bug…you might spend more than a short period of time trying to figure out WTF called the template constructor when you know nobody did. Well, someone did….your broken compiler did.

If, like me, the compiler refuses to tell you what bit of code is calling the template (because there actually isn’t any, though it would be nice to see, “during compilation of default created copy constructor for…,” like GCC often does) you might declare a private copy constructor in anything that derives from said object and see if you get complaints about having no access rather that long tirades of template vomit leading to dead ends. I just did this and ran into a couple places where the objects I didn’t think where ever copied where being copied.

I can’t currently be bothered to find the link, but this has been posted to MS connect. They’ve known about it for a long time. It’s probably not ever going to be fixed. It’s been around at least as far back as 2005 and the above code exhibits the same problem in 2010.

Lambda capture and object lifetime

Posted in Uncategorized on April 6, 2011 by Crazy Eddie

Someone brought up an issue in comp.lang.c++ today that might be unexpected enough to some that it warrants discussion. Consider the following code

include <functional>
struct up_chuck
{
  int x;

  std::function<int()> get_fun() const
  {
    return [=](){return x;}
  }
};

int main()
{
  auto fun = up_chuck().get_fun();
  
  fun();
}

What do you expect to happen here? If you expect the lambda clause to capture a copy of ‘x’ and create a function that returns it then you’d be quite unfortunately wrong. The issue here is that member variables are not captured directly; the variable that is actually captured is ‘this’! What this means of course is that the lambda captures the ‘this’ pointer by value, making a copy of it, and then using that pointer to access ‘x’ and return it. We can prove this with a different main function:

#include <iostream>
int main()
{
  up_chuck uc;
  uc.x = 0;

  auto fun = uc.get_fun();

  std::cout << fun() << std::endl;
  uc.x = 42;
  std::cout << fun() << std::endl;
}

If you expect that the output of this program should be “0\n0\n” then I suggest you run it and verify that you’ll NOT get that output.

So then, what will happen to our original program if we compile and run it? Well, since the lambda makes a copy of the ‘this’ pointer and accesses its x variable to return it, the behavior of the program is undefined. Since the object that ‘this’ points to is a temporary that is destroyed as soon as the line that creates the ‘fun’ variable is complete, the function that ‘fun’ is now makes use of a dangling pointer.

So what can we do in order to avoid running into this problem? The one thing I can think of is to never, ever use default capture clauses in your lambdas. If we’d written our lambda and attempted to capture ‘x’ by value it would become clear that we’ve got a problem. Then we could assign that value locally and capture that new variable and everything would work fine. If we really do need to capture ‘this’ then we do so explicitly and we know that we’re going to have lifetime issues because we’ve captured a pointer. Thus never using the default capture syntax can at least point out some possible problems to us.