Типы возвращаемых возвратов, decltype и constness
Я с радостью экспериментировал с новыми типами возвращаемых возвратов, где я столкнулся с проблемой с этим (упрощенным) кодом
#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
Вы правы, это ошибка. Согласно N3291, раздел 5.1.1, пункт 3:
Если объявление объявляет функцию-член или шаблон функции-члена класса X, выражение представляет собой prvalue типа "указатель на cv-quali-fi-seq X" между необязательным cv-qualifer-seq и концом функция-определение, член-декларатор или декларатор. Он не должен появляться перед необязательным cv-quali-fi-seq и не должен появляться в объявлении статической функции-члена (хотя его тип и категория значения определяются внутри статической функции-члена, поскольку они находятся в нестатической функции-члене), [Примечание: это связано с тем, что совпадение объявлений не происходит до тех пор, пока не будет известен полный декларатор. -end note] В отличие от выражения объекта в других контекстах *, это не обязательно должно быть полного типа для целей доступа членов класса (5.2.5) вне тела функции-члена. [Примечание: видны только участники класса, объявленные до объявления. -end note]
Но это было последнее изменение между последним рабочим проектом и N3291. Таким образом, GCC был прав менее 6 месяцев назад; что опасность написания кода в движущейся спецификации.