Thursday, April 15, 2010

C++ Trivia: Another reason to avoid function-style casts

I was asked to solve a problem that two of my collegues were puzzled by. The source code was short and minimalistic, but the error was not very obvious. Here's the code (modified, because the original included dependencies to Qt):

#include <iostream>
#include <string>

class Test
{
public:
   enum Enum
   {
      First,
      Second,
      Last
   };

   Test(Enum e): m_value(e) { }

   std::string name() const
   {
      switch (m_value)
      {
      case First:
         return "First";
      case Second:
         return "Second";
      case Last:
         return "Last";
      }
      return std::string();
   }

private:
   Enum m_value;
};

int main()
{
   for (int i = 0; i < Test::Last; ++i)
   {
      Test test(Test::Enum(i));
      std::cout << test.name() << std::endl;
   }
}

Can you spot the error without compiling? Don't worry. Even compilers do not agree on this one. Visual C++ 2005 and gcc 4.4.1 both spit out a similar error message in line

"std::cout << test.name() << std::endl;"
. gcc, by the way, has the more helpful error message here:
test.cpp:39: error: request for member ‘name’ in ‘test’, which is of non-class type ‘Test(Test::Enum)’
Does this help you? Have a close look at the code in the for-loop. Only look at the solution when you are out of ideas!

Klick here to see the solution.

The statement
Test test(Test::Enum(i));
is a function declaration. There is a rule in C++ that everything that can be read as a function declaration will be a function declaration. In this particular case, it's a function returning an object of type Test, taking an enum value of type Test::Enum with the parameter name i. It's important to know that you always can put parameter names in parantheses. This means that
void test(int i);
is the same as
void test(int (i));
The failed attempt to cast i to a value of type Test::Enum lead to a construct that looks like a function parameter. So test is a function name, which obviously can't be used in the manner of test.name(). Hint: This wouldn't have happened if static_cast had been used.

1 comment: