С++ 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>&
, затем разыгрывает это.