C ++: борьба с универсальным константным указателем - PullRequest
3 голосов
/ 30 октября 2009

Я столкнулся с некоторыми досадными проблемами с константностью в некотором шаблонном коде, которые в конечном итоге сводятся к следующему наблюдению: по какой-то причине, учитывая тип контейнера STL-ish типа T, const typename T::pointer на самом деле, кажется, не получить постоянный тип указателя, даже если T::pointer эквивалентно T::value_type*.

Следующий пример иллюстрирует проблему. Предположим, у вас есть шаблонная функция, которая принимает контейнер, который должен соответствовать требованиям концепции контейнера произвольного доступа STL.

template <class Container>
void example(Container& c)
{
    const typename Container::pointer p1 = &c[0]; // Error if c is const
    const typename Container::value_type* p2 = &c[0]; 
}

Тогда, если мы передадим эту функцию константному контейнеру ...

const std::vector<int> vec(10);
example(vec);

... мы получаем неверное преобразование из const int* в int*. Но почему const typename Container::pointer не совпадает с const int* в этом примере?

Обратите внимание, что если я изменю const typename Container::pointer на просто typename Container::const_pointer, то он прекрасно скомпилируется, однако, насколько я могу судить, conde_pointer typedef является расширением (я не вижу его упомянутым в стандартных требованиях контейнера C ++ (23.5, таблица 65)), и поэтому я не хочу его использовать.

Так как же я могу получить общий, правильный константный тип указателя из контейнера T? (Я действительно не могу понять, как это сделать, не используя boost :: mpl :: if_ вместе с type_traits, чтобы проверить, является ли контейнер постоянным ... но должен быть менее подробный способ сделать это)

Редактировать: В случае, если это имеет значение, я использую gcc 4.3.2 для компиляции.

Ответы [ 5 ]

17 голосов
/ 30 октября 2009

Это не работает, потому что ваш const не относится к тому, к чему вы относитесь. Например, если у вас есть

typedef int* IntPtr;

тогда

const IntPtr p;

не обозначает

const int* p;

а скорее означает

int* const p;

Typedef-name не является макросом. После того, как "pointerness" типа обернут в typedef-name, его нельзя использовать для создания указателя на тип const. То есть нет абсолютно никакого способа использовать приведенное выше IntPtr typedef-name для получения эквивалента

const int* p;

Вы должны либо явно указать тип (как вы это делали с value_type), либо проверить, определяет ли ваш контейнер другое имя-определения типа, с const, уже обернутым «внутри» (например, const_pointer или что-то вроде вот так).

3 голосов
/ 30 октября 2009

Это:

typename Container::pointer

Имеет тип int* (в нашем случае). Я не знаю терминологию, извините за это, но указатели указывают на тип. То есть Container::pointer является указателем на изменяемый T, и добавление const только сделает этот указатель const (а не указатель на const), потому что Container :: pointer уже определен для указания на изменяемый T.

Кажется, только const_pointer, либо из класса, либо из вашего:

typedef const typename Container::value_type* const_pointer

Будет работать.

2 голосов
/ 30 октября 2009

Требования к распределителю (см. 20.1.5, Таблица 32, «Требования к распределителю») указывают, что распределители для контейнеров STL должны иметь Allocator::const_pointer. Все контейнеры STL (восемь контейнеров, перечисленных в разделе 23) определяют typedef Container::const_pointer как:

typedef typename Allocator::const_pointer const_pointer;

Однако, как вы указали в своем вопросе, контейнеры (кроме этих восьми) не обязательно должны иметь Container::const_pointer typedef.

1 голос
/ 19 декабря 2018

Это возможно при использовании шаблона. Хитрость заключается в том, чтобы сначала удалить указатель, затем применить const к типу и, наконец, применить указатель к типу - например, используя std::remove_pointer yields:

typedef const std::remove_pointer<Container::pointer>::type* PointerToConst;
1 голос
/ 30 октября 2009

Это может быть уместно здесь (из SGI STL ссылка ):

[6] Как и в случае ссылок [5], тип указателя должен иметь ту же семантику, что и указатели C ++, но не обязательно должен быть указателем C ++. «Умные указатели», однако, в отличие от «умных ссылок», возможны. Это связано с тем, что пользовательские типы могут определять оператор разыменования и оператор доступа к элементу указателя, operator * и operator->.
...