Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this C++ template class code work?

I am trying to port Google Test (gtest) code to VxWorks 5.5. The serious drawback is that development environment Tornado 2.2 uses ancient GCC compiler version 2.96.

While analyzing the code I've located part of the code in gtest.h I do not understand! How this C++ template class is functioning?

// ImplicitlyConvertible<From, To>::value is a compile-time bool
// constant that's true iff type From can be implicitly converted to
// type To.
template <typename From, typename To>
class ImplicitlyConvertible {
 private:
  // We need the following helper functions only for their types.
  // They have no implementations.

  // MakeFrom() is an expression whose type is From.  We cannot simply
  // use From(), as the type From may not have a public default
  // constructor.
  static From MakeFrom();

  // These two functions are overloaded.  Given an expression
  // Helper(x), the compiler will pick the first version if x can be
  // implicitly converted to type To; otherwise it will pick the
  // second version.
  //
  // The first version returns a value of size 1, and the second
  // version returns a value of size 2.  Therefore, by checking the
  // size of Helper(x), which can be done at compile time, we can tell
  // which version of Helper() is used, and hence whether x can be
  // implicitly converted to type To.
  static char Helper(To);
  static char (&Helper(...))[2];  // NOLINT

  // We have to put the 'public' section after the 'private' section,
  // or MSVC refuses to compile the code.
 public:
  // MSVC warns about implicitly converting from double to int for
  // possible loss of data, so we need to temporarily disable the
  // warning.
#ifdef _MSC_VER
# pragma warning(push)          // Saves the current warning state.
# pragma warning(disable:4244)  // Temporarily disables warning 4244.

  static const bool value =
      sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
# pragma warning(pop)           // Restores the warning state.
#elif defined(__BORLANDC__)
  // C++Builder cannot use member overload resolution during template
  // instantiation.  The simplest workaround is to use its C++0x type traits
  // functions (C++Builder 2009 and above only).
  static const bool value = __is_convertible(From, To);
#else
  static const bool value =
      sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
#endif  // _MSV_VER
};

When object of this class is created, boolean variable with the name value should contain the answer if template type From is implicitly convertible to template type To. To get the answer, two private functions are used, MakeFrom() and Helper(). But these two functions are only declared here, and I cannot find definition for neither of them. If nothing else, this implementation should not link.

Neither do I understand the syntax of the following

static char (&Helper(...))[2];

Of course, this code compiles just fine (under Microsoft Visual C++ 7.1 or newer or GCC 3.4 or newer) and guys at Google know exactly what they are doing.

Please enlighten me! Not understanding this code will make me go crazy! :)

like image 203
Mitja Kukovec Avatar asked Dec 20 '22 16:12

Mitja Kukovec


1 Answers

This is a standard trick with template programming.

Note that the comments say "by checking the size of Helper(x)": this underscores that the only thing the code does with Helper is evaluate sizeof(Helper(x)) for some x. The sizeof operator does not actually evaluate its argument (it doesn't need to; it only needs to find out how large it is, which is possible using only information available at compile time) and this is why there is no linker error (Helper is never really called).

The syntax that gives you trouble means that Helper is a function that accepts any number and type of parameters and returns a reference to a char[2]. To write a signature for this type of function (a variadic function) one needs to use ellipsis (...) as the specification for the last argument.

Variadic functions are a feature inherited from C that should generally be avoided and that wreaks havoc when used with class types, but in this case it does not matter because -- as mentioned earlier -- Helper will not be actually called.

The class ties this all together by allowing you to use the syntax

ImplicitlyConvertible<From, To>::value

To produce value, the code "fakes" calling Helper and passing it an instance of From as an argument¹. It relies on the compiler's overload resolution to determine if the overload that takes a To would be called in this scenario; if so, the return value of that overload is char which has a guaranteed size of 1 and value ends up being true. Otherwise the variadic overload (which can take any type of argument) is selected, which returns a char[2]. This has a size greater than 1, so value ends up false.


¹ Note that here the "sizeof does not actually evaluate the expression" trick is used again: how do you tell the compiler that the argument to Helper is an instance of From? You could use From(), but then From would need to have a default public constructor for the code to compile. So you just tell the compiler "I have a function MakeFrom that returns a From" -- the function will not be actually called.

like image 112
Jon Avatar answered Dec 24 '22 00:12

Jon