Craptimizing with Const Member Functions

Bheeshmar has pointed out a major flaw in my const-member optimization.


First, some backstory: I recently tried to use a const boost::function in some performance-sensitive code. I did a quick test to measure the overhead of boost::function, and discovered that it was higher than expected. I whipped out WinDbg and traced thru the call to operator()(). The following code was at least partly to blame:


if(empty())
     throw bad_function_call();

Remember that this is a const boost::function, which was initialized with a target and cannot be changed. The empty() check is pointless.

I thought I had found a solution to the problem when I discovered that member-function constness played into overload resolution. Hastily, I wrote a blog entry.

My intent was to provide three, and only three, modes of construction:

  1. Creation of a non-const (and uninitialized) object using Foo::Foo()
  2. Creation of a non-const (and initialized) object using Foo::Foo(T data)
  3. Creation of a const (and initialized) object using Foo::Foo(T data)

What I missed was the fact that you could still instantiate a const Foo without initializing it — via the default constructor:


const Foo f();  // not initialized
f.fubarize()      // uh-oh, calls "fast" fubarize()

This is bad news.

The idea was that non-const Foo object (that is, those created with methods 1 or 2) must always call the slow fubarize(). On the other hand, const Foos should always be initialized upon creation (method 3) and thus can safely use the fast fubarize().

So here’ s my quandry: I know that I can write a “regular” Foo class that always does a “validity” check. I also could write a Bar class which explicitly disables default construction and thus can safely omit the check. What I can’t do (yet) is write one class that does both of these things.

Maybe I need a factory method. I think I’m going to crack open Design Patterns and read the section on “creational patterns” again.

Advertisements


%d bloggers like this: