Question

I've been trying to distinguish cases when the argument passed to constructor is an array or a simple pointer. So, I write this example code

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

// here I create a structure which I intend to use 
// to tell arrays from non-arrays.

template<typename T>
struct TypeTraits {
  static bool const IsArray = false;
};

template<typename T>
struct TypeTraits<T[]> {
  static bool const IsArray = true;
};

// And here I create a container to store a pointer.

template<typename TValue>
class Ptr_container {
  TValue * ptr;
public:
  template<typename T>
  explicit Ptr_container(T from_ptr) :
    ptr (from_ptr)
  {
    cout << std::boolalpha;
    cout << TypeTraits<T>::IsArray << endl;
    cout << "Just to be sure: " << std::is_array<T>::value << endl;
  }
  ~Ptr_container()
  {
    delete ptr;
  }
};

int main () {
  Ptr_container<int> a (new int(2));
  Ptr_container<int> b (new int[2]);
  return 0;
}

And the result is false/false. So, I'm obviously doing something wrong, and the question is, what should I do to distinguish cases when new memory was allocated using new T and new T[N] ?

Update: I've added an "arrayness" check using std::is_array, but it wouldn't change the result. There must be some way to detect "arrayness" during compile-time, because of following: boost::shared_ptr takes a pointer as an argument to constructor and its inner structures are initialized somehow using only that information. And somewhere inside boost::shared_ptr class there is a structure which decides how to delete memory pointed by shared_ptr (using delete or delete[]).

Actually, I'm trying to implement some part of shared_ptr, but I'm failing at the stage of array recognition.

Update 2 I've finally got it figured out, so I'm closing the question. The solution to the problem was to add another parameter to the template and do the initialization of container in the way like

Ptr_container<int, PointerProperty> pa (new int(4));
Ptr_container<int, ArrayProperty> pv (new int[4]);
Was it helpful?

Solution

You absolutely cannot distinguish between pointers returned from new and new[] at compile-time. Not using templates, or standard type traits with compiler hooks, and not any other technique.

The reason for this should be obvious; if it's not, consider the following code: (things don't work like this; I'm just following your example.)

int * p = nullptr;
if (rand() % 2 == 0)
    p = new int;
else
    p = new int [42];

print_is_array (p); // Assume this does the detection and prints something.

How could you (and the compiler) know at compile time whether p will be allocated using new or new[]?

Also, what you call "array" is more properly known as "vector" (not to be confused with std::vector.) Arrays have some compile-time properties (like size) that run-time-allocated blocks of memory don't, even if you happen to construct multiple objects in them (e.g. with new[].)

And, passing an array of Ts (a real, proper, compile-time array) into a function that accepts T *s will "decay" the type and lose the "arrayness". I'm referring here to the way you've defined your constructor, namely it taking a T *.

If you are curious to see how to detect (proper) arrays at compile-time, take a look at the implementation of std::is_array<T> template in the <type_traits> header.

EDIT: By the way, this is what I mean when I say (proper) array:

int a [42];

not this:

int * p = new int [42];

These are two different types, but the first one can "decay" and be implicitly cast into the second type.

OTHER TIPS

As Zeta said in the comment:

typeof (new int) == typeof (new int[]) == int *

However if you need to distinguish between objects of your classes created with new and new[], overloading those operators would make more sense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top