Как проверить, является ли тип определением типа int - PullRequest
5 голосов
/ 13 декабря 2011

В C ++ я хочу иметь класс, конструкторы которого следующие:

class A {
  explicit A(A* other) { ... }
  explicit A(intptr_t other) { ... }
};

Проблема в том, что пользователь инициализируется с помощью

A a(0);

Затем в 64-битной системе компилятор будет жаловаться, что не знает, следует ли преобразовать 0 в A* или в intptr_t, что достаточно справедливо. Поскольку я хочу, чтобы эта простая запись работала, я добавил следующий конструктор:

explicit A(int a) { assert(a==0); ... }

Утверждение состоит в том, что это единственное целое число, для которого имеет смысл. Теперь проблема возникает с 32-битной системой, в которой intptr_t на самом деле ... int! Поэтому теперь система жалуется, что есть два конструктора, принимающих один и тот же тип параметра (что опять-таки достаточно справедливо).

Итак, мой вопрос: есть ли способ с препроцессором обнаружить, что intptr_t на самом деле int и, в этом случае, не компилировать конструктор с int. Или есть другой способ сделать запись A a(0) действительной, не добавляя конструктор с int, но не удаляя ни один из двух первых конструкторов (и не делая их неявными).

Ответы [ 5 ]

3 голосов
/ 13 декабря 2011

Что-то вроде

#if INTPTR_MAX == INT_MAX

может сделать трюк, но это все равно приведет к истине, где long и int имеют одинаковый размер, а ptrint_t является typedef для long. Другая возможность (но не знаю, можете ли вы использовать это или нет) будет использовать uintptr_t, а не intptr_t.

Помимо этого: препроцессор не знает о типах, поэтому проблема не может быть решена там. Вам придется использовать какое-то метапрограммирование хитрость: вы делаете конструктор int шаблоном, используя boost::enable_if, чтобы активировать его, только если аргумент имеет тип int. Если ptrint_t равно int, то активированная функция никогда не будет используется, потому что это никогда не будет лучшим соответствием, чем не шаблон функция с той же подписью. Если ptrint_t не int, то создание шаблона будет лучше соответствовать, когда аргумент имеет тип int. (Обратите внимание, что я никогда не пробовал это сам: мне это нравится должно быть возможно, но я не настолько знаком с boost::enable_if, чтобы будь уверен.)

1 голос
/ 13 декабря 2011

Почему бы вам просто не реализовать конструктор без параметров, который действует так, как если бы other были 0?Если по какой-то причине вы этого не хотите, я предлагаю использовать черты типа при условии, что у вас есть доступ к компилятору C ++ 11 или бусту:

class A { 
public:
    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 

    template <class T>
    explicit A(T other)
    {
        static_assert(std::is_convertible<T, intptr_t>::value, "Could not convert value to intptr_t");
        static_assert(std::is_integral<T>::value, "Argument must be integral");
        intptr_t p = other;
        ...
    }
}; 

Вы можете избавиться от статических утверждений и проверок типов, но тогда вместо ошибки компилятора вы получите предупреждение (в зависимости от вашего уровня предупреждения, может даже превратиться в ошибку), если вы сделаете следующее:

A a(0.0f);
0 голосов
/ 13 декабря 2011

Полагаю, самое простое - объявить все 6 конструкторов (int, long, long long и их варианты без знака) вместо использования intptr_t.

0 голосов
/ 13 декабря 2011

Вы также можете передать параметр, который определяет, какой из них вызывается:

struct TakePtr{};
struct TakeInt{};

class A {
  A(A* other, const TakePtr&) { ... }
  A(intptr_t other, const TakeInt&) { ... }
};

Таким образом, вы можете убедиться, какой конструктор вызывается:

A a2( 0, TakeInt() );     // calls the 2nd constructor, taking int
A a1( &a2, TakePtr() );   // calls the 1st constructor, taking a pointer
0 голосов
/ 13 декабря 2011

есть ли другой способ сделать запись A a(0) действительной

Просто введите конструктор template.

class A {
public:
  template<typename T>
  explicit  A (T t) { assert(t==0); }  // explicit matters ?

  explicit A(A* other) { ... }
  explicit A(intptr_t other) { ... }
};

Это решит ваши 32-битная и 64-битная проблема!

...