with the following code (a boiled-down version of my original code)
#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
}
I get the following error with gcc 4.7.0:
test.cc: In function
decltype (std::abs(x.diff(y))) dist(const A<X>&, const A<Y>&)[withX = double; Y = double; decltype (std::abs(x.diff(y))) = double]’:test.cc:5:5: error:
double A<double>::ais privatehighlighted line: error: within this context
This error message is obviously not very helpful. Is there an error in my code? Or is this a problem with the compiler?
EDIT1: the friend declaration didn’t help.
EDIT2: avoiding using A<Y>::a didn’t help either.
EDIT3: together with EDIT2 finally fixed the problem. The decltype() in the definition of dist() requires the decltype() for A<X>::diff(), which in turn used A<X>::a, which is private in the first context.
EDTI4: Rook’s suggestion of using typename std::common_type<X,Y>::type also works!
EDIT5: but see Jonathan Wakely’s answer to this question
TL;DR: Gcc appears to have a bug where trailing return types on template member functions are not treated as within the class’s scope.
This bug causes gcc to fail when instantiating the template member function
auto diff(A<Y> const&y) const -> decltype(a-y.a)becauseais private and gcc thinks private members are inaccessible here.The code builds fine with clang and VC++, and I don’t see anything trying to access
A<double>::aoutsideA<double>, so it looks like a gcc bug to me.Others have mentioned that
A<X>andA<Y>are different classes, but that’s not the case here, both areA<double>. I believe that means that friendship is not necessary in this case, although to work in the general caseA<X>does need to be friends with other specializations of A.Specifically,
ainy.ais a dependent name so it cannot be looked up untilA<Y>is known. At that point lookup is done, the accessibility is tested and it should be found thatA<double>does have access toA<double>::a.Here’s the exact code I compiled in both clang (svn-3.2) and VC++11 (Since I’m using clang on Windows I can’t
#include <iostream>)This code results in build errors on gcc 4.5 similar to what you describe.
Replacing
with
causes the code to work on gcc 4.5.
This indicates to me a bug where gcc is failing to treat trailing return types as inside the class’s scope. Some testing reveals that the trailing return type must be on a template function to trigger the bug.