Tuesday, April 27, 2010

C++ void template parameter

In C++, void is special. It is an incomplete type, so sizeof(void) is unknown. To maintain compatibility with C, it gives special consideration to void as the sole function argument type if the argument is not assigned to a variable, so the following declarations are equivalent.
int foo(void) { return 42; }
int foo() { return 42; }
However, when void is used as template parameter, and subsequently used as function argument type, GCC rejects it. Consider this class:
template<typename Tp>
class foo {
 public:
  int operator()(Tp) { return 42; }
};
Instantiating foo<void> results in error: invalid parameter type ‘void’.

However, some idioms such as the identity function should work for void. Consider this definition for identity:
template<typename Tp>
class identity {
 public:
  Tp operator()(Tp x) { return x; }
};
Obviously, even when manually substituting Tp for void, the code would not work because variable x would have an incomplete type. However, the idiom can be made consistent again in the case of void using template specialization.
template<>
class identity<void> {
 public:
  void operator()(void) { return; }
};
And you would get identity<void> to behave as expected.

However, what this means is that you'd likely duplicate the definition for a lot of code. It is probably easier to declare a true empty type. Following the convention of ML, we call it unit.
struct unit {};
It can be used with identity like this:
identity<unit> id;
id(unit());
In C++ (unlike C), the empty struct unit has sizeof(unit) == 1, but otherwise the type should behave the same as void. And the code for identity<unit> should behave the same as identity<void>.

No comments: