Сохранять или отражать «контрольный уровень» переменной - PullRequest
2 голосов
/ 04 июня 2011

Есть ли способ в C ++ отразить «количество уровней указателя» переменной (например, int* a - это 1 уровень, int** b - это 2 уровня, а int c - это 0 уровней)

.. Кроме использования typeid и анализа строки, которая получается из этого?

Причина, по которой я спрашиваю, состоит в том, что я использую указатели на функции-члены , и мне нужно знать, вызывать ли функцию-член как obj->func() или obj.func(), во время компиляции.

Ответы [ 7 ]

4 голосов
/ 04 июня 2011

Если obj является T**, то выполнение obj.*foo является некорректным. Так что вам нужно только выяснить, является ли это указателем или не указателем. Вы можете использовать перегрузку для этого.

template<typename T, typename M> void f(T *obj, M m) { // pointer
  (obj->*m)();
}

template<typename T, typename M> void f(T &obj, M m) { // not a pointer
  (obj.*m)();
}

Это имеет недостаток, заключающийся в том, что он работает только с указателями на функции-члены с нулевым параметром и не возвращает значение (если оно есть), которое возвращают эти функции. Вы не можете сделать следующее (что может быть легко реализовано), потому что обе ветви будут проверены на тип

if(is_pointer(obj)) v = (obj->*m)(arg...); else v = (obj.*m)(args...);

Что вы можете сделать - просто вызвать функцию для разыменования вашего объекта, если он является указателем

template<typename T> T &deref(T *t) { return *t; }
template<typename T> T &deref(T &t) { return t; }

Тогда вы можете сказать

v = (deref(obj).*m)(args...);
3 голосов
/ 04 июня 2011

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

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

template <typename T, typename F>
inline void invoke(T& obj, F func)
{
    (obj.*func)();
}

template <typename T, typename F>
inline void invoke(T* obj, F func)
{
    (obj->*func)();
}

Если вам нужно обработать более одного уровня косвенности, вы можете заменить вторую функцию следующим:

template <typename T, typename F>
inline void invoke(T* obj, F func)
{
    invoke(*obj, func);
}

Это будет рекурсивно отбрасывать уровни косвенности до тех пор, пока вы не получите что-то, с чем вы можете вызвать функцию-член.

3 голосов
/ 04 июня 2011

Частичная специализация шаблона скажет вам довольно легко:

template<typename T>
struct PtrLevel
{
    enum { value = 0 };
};

template<typename TTarget>
struct PtrLevel<TTarget*>
{
    enum { value = PtrLevel<TTarget>::value + 1 };
};

Демонстрация: http://ideone.com/ZPH8X

В C ++ 0x вы, вероятно, могли бы использовать decltype и SFINAE для обработки умныхуказатели таким образом, что они имеют ненулевой уровень указателя.

2 голосов
/ 04 июня 2011

Немного волшебства шаблона для спасения:

template<typename T>
struct Depth { enum DepthEnum { value = 0 };};

template<typename T>
struct Depth<T*> { enum DepthEnum{ value = Depth<T>::value +1 };};

template<typename T>
size_t getDepth(T const& o)
{
    return Depth<T>::value;
}

int main()
{
    int         a0;
    int*        a1;
    int**       a2;
    int***      a3;
    int****     a4;
    int*****    a5;
    int******   a6;
    std::cout << getDepth(a0) << std::endl;
    std::cout << getDepth(a1) << std::endl;
    std::cout << getDepth(a2) << std::endl;
    std::cout << getDepth(a3) << std::endl;
    std::cout << getDepth(a4) << std::endl;
    std::cout << getDepth(a5) << std::endl;
    std::cout << getDepth(a6) << std::endl;
}

Вывод:

> g++ test.cpp
> ./a.out
0
1
2
3
4
5
6

Возвращаясь к причине, по которой вы этого хотите.Это кажется немного подозрительным.Ваш компилятор уже знает, какую версию использовать во время компиляции.Помните, что C ++ очень строги в своей типизации, и эта информация известна во время компиляции, поэтому, если вы используете неправильную версию, вы получите ошибку компилятора.

0 голосов
/ 04 июня 2011

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

template <class T> int countPartial(T *x,int depth)
//use template <class T> T& countPartial(T *x)
{
    return countPartial(*x,depth+1);
    //return countPartial(*x);
}

template <class T> int countPartial(T &x,int depth)
//use template <class T> T& countPartial(T &x)
{
    return depth;
    //return x;
}

template <class T> int count(T x)

{
    return countPartial(x,0);
}
0 голосов
/ 04 июня 2011

Вероятно, это shud решить ваши проблемы Т это шаблон. funcptr ptr для работы.

int callfunc( T ptr)
{

depth = 0;

if ( typeof(ptr)!=  typeof(funcptr))

{


depth++;
depth = depth + callfunc(*ptr);
}
return depth;
}
0 голосов
/ 04 июня 2011

Я не знаю ни одного встроенного способа сделать это, но вы могли бы реализовать небольшой шаблонный класс, который имитирует «счетный указатель» в том смысле, что вы можете легко определить свою «глубину указателя», например:

template <typename T> class Ptr {
  private:
    T* ptr;
    int d;
  public:
    // overload all meaningful operators, like * etc.
    // e.g.
    T& operator*() {
      return *T;
    }
    int depth() const {
      return d;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...