14

使用以下代码(我的原始代码的简化版本)

#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>&)中 [with 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()for A<X>::diff(),而后者又使用A<X>::a,它在第一个上下文中是私有的。

EDTI4:Rook 的使用建议typename std::common_type<X,Y>::type也有效!

EDIT5:但请参阅 Jonathan Wakely 对这个问题的回答

4

5 回答 5

9

TL;DR: Gcc似乎有一个错误,即模板成员函数上的尾随返回类型不被视为在类的范围内。

此错误导致 gcc 在实例化模板成员函数时失败,auto diff(A<Y> const&y) const -> decltype(a-y.a)因为a它是私有的,并且 gcc 认为私有成员在此处不可访问。


该代码使用 clang 和 VC++ 构建得很好,我没有看到任何试图访问A<double>::a外部的东西A<double>,所以对我来说它看起来像是一个 gcc 错误。

其他人已经提到A<X>并且A<Y>是不同的类,但这里不是这种情况,两者都是A<double>. 我相信这意味着在这种情况下不需要友谊,尽管在一般情况下工作A<X>确实需要与 A 的其他专业成为朋友。

具体来说,ain是一个从属名称,因此在已知y.a之前无法查找它。A<Y>此时查找完成,可访问性被测试,应该发现A<double>确实可以访问A<double>::a.

这是我在 clang (svn-3.2) 和 VC++11 中编译的确切代码(因为我在 Windows 上使用 clang,所以我不能#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 未能将尾随返回类型视为在类的范围内。一些测试表明尾随返回类型必须在模板函数上才能触发错误。

于 2012-06-15T14:44:38.977 回答
8

您的代码有错误:

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

编辑:也就是说,在您的特定情况下,Xand Yare both double,所以我天真地期望它能够编译。请注意,在最好的情况下,这种构造应该只在XY相同时编译,这可能不是您想要的。

于 2012-06-15T14:45:13.920 回答
2

您的函数在语句中从类外部auto diff(A<Y> const& y)访问private变量。A::adecltype

如果您想以您在函数中的方式使用它,您A::a应该这样做。publicdiff

编辑:似乎友谊解决方案更适合这个问题,然后将其公开。

于 2012-06-15T14:46:19.677 回答
2

auto diff(A<Y> const&y) const -> decltype(a-y.a)是问题;不管别的,ifXY是不同的类型,A<X>并且A<Y>是不同的类型,不能偷看对方的隐私。模板不是协变的!

这里的具体错误可能是 GCC 的怪癖(因为它没有发现XY输入你的 diff 函数?)将永远不会工作,无论编译器如何。

于 2012-06-15T14:53:56.523 回答
0

正如我现在已经弄清楚的那样(另请参阅问题的编辑)问题是什么。事实证明,所有答案(到目前为止)都没有解决(完整)问题。

本质上,对decltype全局函数的表达式求值最终需要私有成员A<X>::a(通过它出现在 的decltype表达式中A<x>::diff())。这就提出了下一个问题,即标准对此有何规定。

于 2012-06-15T15:37:12.993 回答