Error with using decltype() in C++11 (creating opaque error message in gcc 4.7.0)

穿精又带淫゛_ 提交于 2019-12-05 12:19:44

问题


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>&) [with X = double; Y = double; decltype (std::abs(x.diff(y))) = double]’:

test.cc:5:5: error: double A<double>::a is private

highlighted 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


回答1:


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) because a is 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>::a outside A<double>, so it looks like a gcc bug to me.

Others have mentioned that A<X> and A<Y> are different classes, but that's not the case here, both are A<double>. I believe that means that friendship is not necessary in this case, although to work in the general case A<X> does need to be friends with other specializations of A.

Specifically, a in y.a is a dependent name so it cannot be looked up until A<Y> is known. At that point lookup is done, the accessibility is tested and it should be found that A<double> does have access to A<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>)

#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);
}

This code results in build errors on gcc 4.5 similar to what you describe.

Replacing

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

with

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

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.




回答2:


There is an error with your code:

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

Here, A<Y> is a different type, so A<X> cannot see it's a data member. Only A<Y> can see A<Y>::a.

Edit: that said, in your particular case, X and Y are both double, so I would naively expect that to compile. Note that in the best of cases, this construction should only compile when X and Y are the same, which may not be what you want.




回答3:


auto diff(A<Y> const&y) const -> decltype(a-y.a) is the problem; regardless of anything else, if X and Y are different types, A<X> and A<Y> are different types and cannot peek at each other's privates. Templates are not covariant!

The specific error here might be an eccentricity of GCC (in that it doesn't spot that X is the same type as Y) but the more general case where you might be trying to diff two different types (and why else would you have a separate template type in your diff function?) will never work, regardless of compiler.




回答4:


Your function auto diff(A<Y> const& y) access the private variable A::a from outside the class in your decltype statement.

Your A::a should be public if you want to use it the way you do in your diff function.

EDIT: it seems the friendship solution is much better suited for this problem, then just making it public.




回答5:


As I have figured out by now (see also the edits to the question) what the problem was. It turns out that all answers (sofar) didn't solve the (full) problem.

Essentially, the evaluation of the decltype expression for the global function required eventually the private member A<X>::a (through its occurrence in the decltype expression of A<x>::diff()). This opens the next question about what the standard says about this.



来源:https://stackoverflow.com/questions/11052842/error-with-using-decltype-in-c11-creating-opaque-error-message-in-gcc-4-7-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!