Конечные типы возвращаемых данных, decltype и const -ess - PullRequest
16 голосов
/ 31 августа 2011

Я весело экспериментировал с новыми типами конечного возврата, где столкнулся с проблемой с этим (упрощенным) кодом

#include <list>

class MyContainer{
  std::list<int> ints;

  auto begin( ) -> decltype(ints.begin())
  {
    return ints.begin();
  }

  auto begin( ) const -> decltype(ints.begin())
  {
    return ints.begin();
  }
};

Игнорируйте факт бессмысленности этого кода. Важной частью является ошибка компилятора, генерируемая при использовании GCC 4.6.1 (с флагом -std=c++0x):

In member function 'std::list<int>::iterator MyContainer::begin() const':
error: could not convert '((const MyContainer*)this)->MyContainer::ints.std::list<_Tp, _Alloc>::begin [with _Tp = int, _Alloc = std::allocator<int>, std::list<_Tp, _Alloc>::const_iterator = std::_List_const_iterator<int>]()' from 'std::list<int>::const_iterator {aka std::_List_const_iterator<int>}' to 'std::list<int>::iterator {aka std::_List_iterator<int>}'

В случае, если вы не являетесь поклонником ошибок, связанных с шаблонами, краткий рассказ о том, что в теле const версии MyContainer::begin выражение ints.begin() возвращает значение типа std::list<int>::const_iterator (так как ints - это const в таком контексте). Однако decltype(ints.begin()) создает тип std::list<int>::iterator, то есть decltype игнорирует квалификатор const метода begin при определении типа выражения. Неудивительно, что конфликт типов является результатом.

Мне кажется, это ошибка в компиляторе GCC. decltype имеет смысл только соблюдать квалификатор const и производить тип const_iterator. Кто-нибудь может подтвердить или опровергнуть (возможно, даже объяснить) это? Возможно, я что-то упускаю из механики decltype, но это выглядит довольно простым сценарием.

Примечание: насколько я могу судить, такое же поведение сохраняется не только для std::list<int>, но и для любого типа с функциями-членами, перегруженными на const -ness, которые возвращают несовместимые типы.

1 Ответ

10 голосов
/ 31 августа 2011

Вы правы, это ошибка. Согласно N3291, раздел 5.1.1, пункт 3:

Если объявление объявляет функцию-член или шаблон функции-члена класса X, выражение this является значением типа «указатель на cv-qualifer-seq X» между необязательным cv-qualifer-seq и концом определение функции, член-декларатор или декларатор. Он не должен появляться перед необязательным cv-qualifer-seq и не должен появляться в объявлении статической функции-члена (хотя ее тип и категория значений определяются внутри статической функции-члена, так как они находятся внутри нестатической функции-члена) , [Примечание: это потому, что сопоставление объявлений не происходит до тех пор, пока не будет известен полный описатель. - примечание конца] В отличие от выражения объекта в других контекстах, * это не обязательно должно быть полного типа для целей доступа к членам класса (5.2.5) вне тела функции-члена. [Примечание: видны только члены класса, объявленные до объявления. —Конечная записка]

Но это было недавнее изменение между последним рабочим проектом и N3291. Таким образом, GCC был прав менее 6 месяцев назад; это опасность написания кода для движущейся спецификации.

...