ошибка определения универсального возвращаемого типа в C++11

В контексте приложения C++14 я использую схему, которую можно продолжить следующим образом (минимальный воспроизводимый тест):

template <class Container>
struct LocateFunctions {    
  auto get_it() const // <-- here is the problem
  {
    auto ret = typename Container::Iterator();
    return ret;
  }
};

template <typename T>
struct A : public LocateFunctions<A<T>> {    
  struct Iterator {};
};

int main() {  
  A<int> a;
}

Этот подход отлично компилируется и работает в C++14 с компиляторами GCC и Clang.

Теперь я хочу перенести свое приложение на Windows, и для этого я использую MinGW. К сожалению, его последняя версия содержит GCC 4.9, который не компилирует C++14. Это не кажется серьезной проблемой, потому что я могу переписать конструкции C++14 на C++11. Итак, я переписываю метод get_it() следующим образом:

typename Container::Iterator get_it() const
{ 
  auto ret = typename Container::Iterator();
  return ret;
}

К сожалению, он не компилируется. Оба компилятора выдают следующую ошибку:

error: no type named ‘Iterator’ in ‘struct A<int>’
   typename Container::Iterator get_it() const
                            ^

Я также пробовал:

auto get_it() const -> decltype(typename Container::Iterator())
{ 
  auto ret = typename Container::Iterator();
  return ret;
}

но я получаю точно такую ​​же ошибку.

Поскольку два компилятора не могут распознать тип возврата, я полагаю, что определить его невозможно. Но я действительно не знаю, почему.

Может кто-нибудь объяснить мне, почему бы не скомпилировать и, в конечном итоге, способ рефакторинга в С++ 11, который компилируется?


person lrleon    schedule 22.12.2015    source источник


Ответы (1)


Вы используете CRTP; LocateFunctions создается с неполной специализацией A (A<int>), поэтому доступ к членам этой специализации дает довольно вводящее в заблуждение сообщение об ошибке ("нет... названо... в..." вместо "... неполное"). Однако в вашем примере функция temploid get_it только (если когда-либо) создается после того, как A<int> действительно определена, что делает спецификатор имени типа хорошо сформировался.

Что касается обходного пути, попробуйте добиться аналогичного эффекта, например. через

template <typename T=Container>
typename T::Iterator get_it() const
{
    static_assert(std::is_same<T, Container>{}, "You ain't supposed to supply T!");
    auto ret = typename T::Iterator();
    return ret;
}

Демо с GCC 4.9.

person Columbo    schedule 22.12.2015
comment
Темплоид? Это слово? - person Quentin; 22.12.2015
comment
@Квентин Да. Обозначает нешаблонные члены шаблонов, затронутые определенным DR, открытым 15 лет назад. - person Columbo; 22.12.2015
comment
Большое спасибо Columbo за объяснение, обходной путь и издание. - person lrleon; 23.12.2015
comment
@lrleon Рад, что смог помочь :-) - person Columbo; 23.12.2015