Ошибка при использовании 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: но см. Джонатан Вакели на этот вопрос
Ответы
Ответ 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()
). Это открывает следующий вопрос о том, что стандарт говорит об этом.