С++ 11: Как получить тип, на который указывает указатель или итератор?

Чтобы быть более конкретным, предположим, что я пишу template<class Pointer> class Foo, и я хочу объявить typedef внутри класса для типа, который *p имел бы, если p имел тип Pointer.

В С++ 03, насколько мне известно, единственный способ сделать это - что-то вроде

typename std::iterator_traits<Pointer>::reference

Недостатком этого метода является то, что он не будет работать, если Pointer - это какой-то пользовательский тип итератора, и автор забыл расширить std::iterator или иначе определить специализацию std::iterator_traits.

В С++ 11 мой коллега предложил

decltype(*Pointer())

Но это не сработает, если Pointer не является конструктивным по умолчанию, поэтому он изменил это на

decltype(**(Pointer*)0)

Я пробовал это, и это сработало, но потом я подумал, что это выглядит немного, потому что оно связано с разыменованием нулевого указателя и, следовательно, может быть не совместимо со стандартами.

Можем ли мы сделать лучше?

Ответы

Ответ 1

Вы правы, чтобы быть осторожным в вопросе о разыменовании нулевого указателя, но факт в том, что здесь все в порядке! decltype не оценивает его операнд, поэтому разыменование нулевого указателя внутри вполне допустимо.

Собственное решение, однако, std::declval, введенное внутри <utility> в С++ 11:

decltype(*std::declval<Pointer>())

Ответ 2

В С++ 03 вы можете написать простую конструкцию, которая удалит все указатели из заданного типа:

template<typename T>
struct ActualType { typedef T type; };
template<typename T>
struct ActualType<T*> { typedef typename ActualType<T>::type type; };

Если вы пройдете int* или int**, тогда, наконец, ActualType<T>::type будет сжиматься до int.

Вот демон ;

Ответ 3

Использовать итераторы с отступлением SFINAE к разыменованию.

Вместо того, чтобы разыменовывать нуль, создайте функцию шаблона, которая возвращает std::decay<T>&, затем разыгрывает это.