【问题标题】:Error with using decltype() in C++11 (creating opaque error message in gcc 4.7.0)在 C++11 中使用 decltype() 时出错(在 gcc 4.7.0 中创建不透明的错误消息)
【发布时间】:2012-06-15 14:38:22
【问题描述】:

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

#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&lt;X&gt;&amp;, const A&lt;Y&gt;&amp;) [with X = double; Y = double; decltype (std::abs(x.diff(y))) = double]':

test.cc:5:5: 错误:double A&lt;double&gt;::a 是私有的

突出显示的行:错误:在此上下文中

这个错误信息显然不是很有帮助。我的代码有错误吗?还是编译器的问题?

EDIT1:朋友声明没有帮助。

EDIT2:避免使用A&lt;Y&gt;::a 也无济于事。

EDIT3:与EDIT2一起终于解决了问题dist() 定义中的 decltype() 需要 decltype() 用于 A&lt;X&gt;::diff(),而 A&lt;X&gt;::diff() 又使用 A&lt;X&gt;::a,在第一个上下文中是私有的。

EDTI4:Rook 建议使用typename std::common_type&lt;X,Y&gt;::type 也有效!

EDIT5:但请参阅 Jonathan Wakely 对this question的回答

【问题讨论】:

  • 使用 gcc-4.3 您的代码也无法编译。有关返回的错误,请参阅 this link
  • @PenguinCoder:gcc-4.3 不支持 C++11(又名 C++0x)。这个问题是关于 gcc-4.7 的。
  • @JohnDibling:我不会关心模板代码中的 MSVC,它没有实现两阶段查找,因此无法检查许多事故。然而,Clang 拒绝它的事实是一个红鲱鱼。
  • 还有一个问题要问大家... std::common_type&lt;X, Y&gt; 会更适合diff() 吗? 看起来应该是这样,但我无法在 gcc 4.4 或 vs2010 中编译它...
  • @Rook:不错的选择!我认为可以安全地假设这是一个 gcc 错误,因为没有理由稍后应该允许在 decltype 中拒绝的表达式出现在正文中。不一致。

标签: c++ c++11 auto decltype


【解决方案1】:

TL;DR: Gcc appears to have a bug 其中模板成员函数的尾随返回类型不被视为在类的范围内。

这个错误导致 gcc 在实例化模板成员函数 auto diff(A&lt;Y&gt; const&amp;y) const -&gt; decltype(a-y.a) 时失败,因为 a 是私有的,而 gcc 认为私有成员在此处无法访问。


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

其他人提到A&lt;X&gt;A&lt;Y&gt; 是不同的类,但这里不是这样,两者都是A&lt;double&gt;。我认为这意味着在这种情况下不需要友谊,尽管在一般情况下工作 A&lt;X&gt; 确实需要与 A 的其他专业成为朋友。

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

这是我在 clang (svn-3.2) 和 VC++11 中编译的确切代码(因为我在 Windows 上使用 clang,所以无法#include &lt;iostream&gt;

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

此代码导致build errors on 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

导致代码为work on gcc 4.5

这向我表明 gcc 未能将尾随返回类型视为类范围内的错误。 Some testing reveals 尾随返回类型必须在模板函数上才能触发错误。

【讨论】:

  • 不,请参阅其他答案。只是A&lt;X&gt;A&lt;Y&gt;是不同的类型,所以不能访问对方的私有成员
  • 啊,有趣。我会说diff 定义不明确,因为它通常无法访问y.a(除非Y 恰好是X);但我不会打赌它是否应该在声明或实例化时被诊断出来。
  • @MatthieuM。我相信如果有 no 合法实例化,那么它的格式不正确,可以在声明或实例化时诊断。但是有合法的实例化,所以我认为应该只对那些不合法的实例化进行诊断。
  • @MatthieuM。显然,模板不能仅仅因为存在非法的 some 实例而被视为格式错误。这个模板只要求y.a 是合法的,就像template&lt;typename T&gt; void foo(T t) { t.bar(); } 要求t.bar() 是合法的一样。
  • @bames53:是的,这似乎更好。毕竟,如果我们谈论的不是 A&lt;Y&gt; some A 的实例化可能是 friend 而不是其他的,那么在声明阶段的检查将很难正确.
【解决方案2】:

您的代码有错误:

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

这里,A&lt;Y&gt; 是一个不同的类型,所以 A&lt;X&gt; 看不到它的 a 数据成员。只有A&lt;Y&gt; 可以看到A&lt;Y&gt;::a

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

【讨论】:

  • 既然在VS2010中编译正确,那么是不是VS2010的bug?
  • 这是我的第一个想法,但我有点怀疑,因为在他的特殊情况下(没关系,你的编辑正是我要说的)
  • @Patrick 我不会假设因为它是在 VS2010 上编译的,所以该错误在 GCC 中。但是现在我不能确定它是否是一个错误。这些东西都很新……
【解决方案3】:

auto diff(A&lt;Y&gt; const&amp;y) const -&gt; decltype(a-y.a) 是问题所在;不管别的,如果XY是不同的类型,A&lt;X&gt;A&lt;Y&gt;是不同的类型,不能偷看对方的隐私。模板不是协变的!

这里的具体错误可能是 GCC 的偏心(因为它没有发现 XY 的类型相同)但更一般的情况是您可能试图区分两种不同的类型(以及为什么要在 diff 函数中使用单独的模板类型?)无论编译器如何,都将永远无法工作。

【讨论】:

  • 虽然您对包含问题的行是正确的,但您对原因是错误的。导致问题的是ay.a的使用,不是A&lt;Y&gt;::aA&lt;X&gt;的访问,而是decltype()在全局函数dist()中的访问。
  • 是的,嵌套问题。这是一个非常复杂的问题!
【解决方案4】:

您的函数auto diff(A&lt;Y&gt; const&amp; y) 在您的decltype 语句中从类外部访问private 变量A::a

如果您想像在 diff 函数中那样使用它,您的 A::a 应该是 public

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

【讨论】:

  • 我相信尾随返回类型在类范围内。
  • getter 和公开它是一样的,我不欣赏你讨厌的 cmets
  • getter 与将其公开不同,因为您仍然没有写入权限。但是在这种情况下,除非没有其他方法可以解决问题,否则这些选项都不应考虑,因为这将是类接口的变化。
  • 友谊解决方案和其他解决方案都没有帮助我避免在A&lt;X&gt; 中使用A&lt;Y&gt;::a,请参阅编辑
【解决方案5】:

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

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

【讨论】:

    猜你喜欢
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多