Bas in het Veld
Bas in het Veld

Reputation: 1312

Default initializing a pointer in a template

In the following code, Get() returns the default value of the template type. I was wondering why and how this is correct/defined for pointer types

#include <iostream>
#include <string>

template <typename ValueType>
inline ValueType Get()
{
   return ValueType();   
}

int main()
{
   int* IntPtr = Get<int*>();
   //int* IntPtr2 = (int*)(); // Invalid
   //int* IntPtr3 = (int*){}; // Valid
   
   std::cout << (IntPtr == nullptr ? "nullptr" : "non-nullptr") << std::endl;
   
   std::cin.get();
   
   return 0;
}

When the compiler evaluates ValueType() in Get, I would have expected it to become (int*)(), but since this is invalid, is it treated differently?

Upvotes: 3

Views: 449

Answers (1)

rustyx
rustyx

Reputation: 85371

When the compiler evaluates ValueType() in Get, I would have expected it to become (int*)(), but since this is invalid, is it treated differently?

A C++ template, while being very similar to a macro, does not perform a literal replacement. ValueType remains an actual type within the scope of the template. So yes, return ValueType() would be treated differently from (int*)(), more like using ValueType = int*; return ValueType();

As to why using ValueType = int*; ValueType(); works when (int*)() doesn't:

In C++, an expression of the form T() (when T is a type) is value-initialization syntax which creates a nameless object of type T.

During value-initialization, non-class types are zero-initialized.

The problem is (int*)() does not have the form T(), but (T)(). (int*)() is parsed as a cast to int*, but then it's missing the value to cast. Which is exactly what the compiler says in the error:

<source>:2:27: error: expected expression
    int* IntPtr2 = (int*)();
                          ^

For the same reason using T = int; int x = (T)(); also won't compile.

Similarly int* IntPtr3 = (int*){}; is also invalid C++ (GCC compiles it, but e.g. MSVC won't).

Some possible workarounds:

  1. typedef the pointer to some type, then you'll be able to use the T() form normally:

     using T = int*;
     T ptr = T();
    
  2. Use copy-list-initialization syntax:

     int* ptr = {};
    
  3. Use direct-list-initialization syntax:

     int* ptr{};
    

Note that the direct-initialization form int* ptr(); is also unusable as it is subject to most vexing parse.

Upvotes: 2

Related Questions