Ошибка при использовании decltype() в С++ 11 (создание непрозрачного сообщения об ошибке в gcc 4.7.0) - программирование
Подтвердить что ты не робот

Ошибка при использовании decltype() в С++ 11 (создание непрозрачного сообщения об ошибке в gcc 4.7.0)

со следующим кодом (своя версия моего исходного кода)

#include <iostream>
#include <cmath>

template <typename> class A;                  // edit 1 following Mark & Matthieu
template <typename X> class A {
  X a;
  template <typename> friend class A;         // edit 1 following Mark & Matthieu
public:
  A(X x) : a(x) {}
  X get() const { return a; }                // edit 2 to avoid using A<Y>::a
  template <typename Y>
  auto diff(A<Y> const& y) const
    -> decltype(a - y.a)                       // original code causing error with gcc
    -> typename std::common_type<X, Y>::type  // alternative following Rook
    -> decltype(this->get() -                 // edit 3 not using A<X>::a
                y.get())                     // edit 2 not using A<Y>::a
  { return a - y.get(); }
};

template <typename X, typename Y>
inline auto dist(A<X> const& x, A<Y> const& y) -> decltype(std::abs(x.diff(y)))
{ return std::abs(x.diff(y)); }

int main()
{
  A<double> x(2.0), y(4.5);
  std::cout << " dist(x,y)=" << dist(x,y) << '\n'; // <-- error here
}

Я получаю следующую ошибку с gcc 4.7.0:

test.cc: В функции decltype (std::abs(x.diff(y))) dist(const A<X>&, const A<Y>&)X = double; Y = double; decltype (std::abs(x.diff(y))) = double]:

test.cc:5:5: ошибка: double A<double>::a является закрытой

выделенная строка: ошибка: в этом контексте

Это сообщение об ошибке явно не очень полезно. Есть ли ошибка в моем коде? Или это проблема с компилятором?

EDIT1: объявление друга не помогло.

EDIT2: избегать использования A<Y>::a тоже не помогло.

EDIT3: вместе с EDIT2 наконец исправлена ​​проблема. decltype() в определении dist() требует decltype() для A<X>::diff(), который, в свою очередь, использовал A<X>::a, который является приватным в первом контексте.

EDTI4: предложение ладьи с использованием typename std::common_type<X,Y>::type также работает!

EDIT5: но см. Джонатан Вакели на этот вопрос

4b9b3361

Ответ 1

TL; DR: Gcc появляется ошибка, в которой возвращаемые типы возвращаемых элементов в функциях члена шаблона не обрабатываются как в пределах класса.

Эта ошибка приводит к сбою gcc при создании экземпляра функции-члена шаблона auto diff(A<Y> const&y) const -> decltype(a-y.a), потому что a является приватным, а gcc считает, что закрытые члены недоступны здесь.


Код строит отлично с clang и VС++, и я не вижу ничего, пытающегося получить доступ к A<double>::a вне A<double>, поэтому он выглядит как gcc-ошибка для меня.

Другие отметили, что A<X> и A<Y> - разные классы, но это не так, оба они A<double>. Я считаю, что это означает, что в этом случае дружба не нужна, хотя для работы в общем случае A<X> нужно дружить с другими специализациями A.

В частности, a в y.a является зависимым именем, поэтому его нельзя найти до тех пор, пока A<Y> не будет известен. В этот момент выполняется поиск, проверяется доступность и должно быть установлено, что A<double> имеет доступ к A<double>::a.

Вот точный код, который я скомпилировал как в clang (svn-3.2), так и в VС++ 11 (Поскольку я использую clang для Windows, я не могу #include <iostream>)

#include <cmath>

template<typename X> class A {
  X a;
public:
  A(X x) : a(x) {}
  template<typename Y>
  auto diff(A<Y> const&y) const -> decltype(a-y.a)
  { return a-y.a; }
};

template<typename X, typename Y>
inline auto dist(A<X> const&x, A<Y> const&y) -> decltype(std::abs(x.diff(y)))
{ return std::abs(x.diff(y)); }

int main()
{
  A<double> x(2.0), y(4.5);
  return (int) dist(x,y);
}

Этот код приводит к сбоям сборки gcc 4.5, аналогичным тому, что вы описываете.

Замена

auto diff(A<Y> const&y) const -> decltype(a-y.a)

с

auto diff(A<Y> const&y) const -> typename std::common_type<X,Y>::type

заставляет код работать с gcc 4.5.

Это указывает на ошибку, когда gcc не обрабатывает возвращаемые типы возвращаемого типа, как внутри области класса. В некоторых тестах показано, что тип возвращаемого возврата должен находиться в функции шаблона, чтобы вызвать ошибку.

Ответ 2

Произошла ошибка с вашим кодом:

template<typename Y>
  auto diff(A<Y> const&y) const -> decltype(a-y.a)
  { return a-y.a; }

Здесь A<Y> - это другой тип, поэтому A<X> не может видеть этот элемент a. Только A<Y> может видеть A<Y>::a.

Изменить: в вашем конкретном случае X и Y являются double, поэтому я бы наивно ожидал, что это скомпилируется. Обратите внимание, что в лучших случаях эта конструкция должна компилироваться только тогда, когда X и Y совпадают, что может и не быть тем, что вы хотите.

Ответ 3

Ваша функция auto diff(A<Y> const& y) получает доступ к переменной private A::a вне класса в инструкции decltype.

Ваш A::a должен быть public, если вы хотите использовать его так, как вы делаете в своей функции diff.

EDIT: похоже, решение дружбы намного лучше подходит для этой проблемы, а затем просто публикует ее.

Ответ 4

auto diff(A<Y> const&y) const -> decltype(a-y.a) - проблема; независимо от чего-либо еще, если X и Y являются разными типами, A<X> и A<Y> являются разными типами и не могут подглядывать друг к другу рядовых. Шаблоны не ковариантны!

Конкретная ошибка здесь может быть эксцентриситетом GCC (в том, что она не указывает, что X - это тот же тип, что и Y), но более общий случай, когда вы можете попробовать разграничить два разных типа ( и почему еще у вас будет отдельный тип шаблона в вашей функции diff?) никогда не будет работать, независимо от компилятора.

Ответ 5

Как я уже выяснил (см. также изменения к вопросу), в чем проблема. Оказывается, что все ответы (sofar) не решают (полную) проблему.

По существу, оценка выражения decltype для глобальной функции, требуемой в конечном счете частным членом A<X>::a (через его появление в выражении decltype A<x>::diff()). Это открывает следующий вопрос о том, что стандарт говорит об этом.