【问题标题】:prototype mismatch with decltype and auto原型与 decltype 和 auto 不匹配
【发布时间】:2023-03-12 17:28:01
【问题描述】:

考虑以下类:

class MyClass
{
    int _id;
public:
    decltype(_id) getId();
};

decltype(MyClass::_id) MyClass::getId()
{
    return _id;
}

它编译得很好。

但是,当我用它制作模板类时:

template <class T>
class MyClass
{
    int _id;
public:
    decltype(_id) getId();
};

template <class T>
decltype(MyClass<T>::_id) MyClass<T>::getId()
{
    return _id;
}

我明白了:

test.cpp:10:27: error: prototype for 'decltype (MyClass<T>::_id) MyClass<T>::getId()' does not match any in class 'MyClass<T>'
 decltype(MyClass<T>::_id) MyClass<T>::getId()                                                                                
                           ^
test.cpp:6:19: error: candidate is: decltype (((MyClass<T>*)(void)0)->MyClass<T>::_id) MyClass<T>::getId()
     decltype(_id) getId();
                   ^

为什么会这样?
为什么会有不同的类型

  • decltype (MyClass&lt;T&gt;::_id) MyClass&lt;T&gt;::getId()
  • decltype (((MyClass&lt;T&gt;*)(void)0)-&gt;MyClass&lt;T&gt;::_id)

我可以通过在类中定义主体来解决它:

template <class T>
class MyClass
{
    int _id;
public:
    decltype(_id) getId() { return _id; }
};

尾随返回类型也有类似的问题:

template <class T>
class MyClass
{
    int _id;
public:
    auto getId() -> decltype(_id);
};

template <class T>
auto MyClass<T>::getId() -> decltype(MyClass<T>::_id)
{
    return _id;
}

错误:

test.cpp:10:6: error: prototype for 'decltype (MyClass<T>::_id) MyClass<T>::getId()' does not match any in class 'MyClass<T>'
 auto MyClass<T>::getId() -> decltype(MyClass<T>::_id)
      ^
test.cpp:6:10: error: candidate is: decltype (((MyClass<T>*)this)->MyClass<T>::_id) MyClass<T>::getId()
     auto getId() -> decltype(_id);
          ^
  • decltype (MyClass&lt;T&gt;::_id) MyClass&lt;T&gt;::getId()
  • decltype (((MyClass&lt;T&gt;*)this)-&gt;MyClass&lt;T&gt;::_id) MyClass&lt;T&gt;::getId()

g++ 5.3.0

【问题讨论】:

  • 在声明中简单地使用decltype( MyClass&lt;T&gt;::_id ) 而不是decltype( _id ) 为我修复了它。 (也是 gcc 5.3.0)。我不知道如何解释这个证据。基本上,只要确保声明和定义尽可能相似——即在两者中都使用decltype( MyClass&lt;T&gt;::_id )

标签: c++ templates c++14 auto decltype


【解决方案1】:

根据草案标准N4582§5.1.1/p13 General [expr.prim.general]Emphasis Mine):

表示非静态数据成员或非静态的 id 表达式 只能使用类的成员函数:

(13.1) — 作为类成员访问 (5.2.5) 的一部分,其中对象 表达式引用成员的类63 或类 派生自该类,或

(13.2) — 形成一个指向成员 (5.3.1) 的指针,或

(13.3) — 如果该 id 表达式表示一个非静态数据成员并且它 出现在未计算的操作数中。 [示例:

struct S {
int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK

—结束示例]

63) 这也适用于对象表达式是隐式的 (*this) (9.3.1).

同样来自 §7.1.6.2/p4 简单类型说明符 [dcl.type.simple]Emphasis Mine):

对于表达式edecltype(e) 表示的类型定义为 如下:

(4.1) — 如果 e 是一个未加括号的 id 表达式或未加括号的 类成员访问(5.2.5),decltype(e)是实体的类型 由 e 命名。如果没有这样的实体,或者如果 e 命名了一组 函数重载,程序格式不正确;

(4.2) — 否则,如果 e 是 xvalue,decltype(e)T&amp;&amp;,其中 Te 的类型;

(4.3) — 否则,如果 e 是左值,decltype(e)T&amp;,其中 Te的类型;

(4.4) — 否则,decltype(e)e 的类型。

decltype 说明符的操作数是未计算的操作数(第 5 条)。

[示例:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17; // type is const int&&
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4 = x3; // type is const double&

—结束示例] [注:确定类型的规则涉及 decltype(auto) 在 7.1.6.4 中指定。 ——尾注]

因此,由于decltype 是未计算的操作数,因此代码是合法的,应该可以编译。

一个干净的解决方法是使用decltype(auto)

template<typename T>
class MyClass {
  int _id;  
public:
  decltype(auto) getId();
};

template<typename T>
decltype(auto) MyClass<T>::getId() {
  return _id;
}

以上代码被 GCC/CLANG/VC++ 接受。

【讨论】:

    【解决方案2】:

    这似乎是g++ 错误。

    我已经在 Visual Studio 2015 中尝试过您的代码:

    构建:1 成功,0 失败,0 最新,0 跳过

    编辑:我找到了workaround

    #include <iostream>
    
    template <class T>
    class MyClass
    {
        T _id = {0};
    public:
        decltype(((MyClass<T>*)nullptr)->_id) getId();
    };
    
    template <class T>
    decltype(((MyClass<T>*)nullptr)->_id) MyClass<T>::getId()
    {
        return _id;
    }
    
    int main()
    {
        MyClass<int> f;
        auto n = f.getId();
    
        std::cout << n << '\n'; // output: 0
    }
    

    输出:

    0
    

    【讨论】:

    • 在 GCC Clang 中失败并在 VC++ 中工作的代码可能是错误的,TBH。
    • 如果它甚至用VC++编译并不意味着它是g++中的一个错误。
    • @ildjarn 代码用 Clang 编译,demo
    • @sergej :当您将 int 设为类内 getId 声明的返回类型时,则不会。
    【解决方案3】:

    好像是 GCC Bug 57712

    错误描述中的示例代码:

    struct Test {
      int method(int value) { return value; }
    
      template <typename T>
      auto test(T value) -> decltype(this->method(value));
    };
    
    template <typename T>
    auto Test::test(T value) -> decltype(this->method(value)) {
      return this->method(value);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-02
      • 1970-01-01
      • 1970-01-01
      • 2011-10-15
      • 2017-03-18
      • 2021-11-18
      • 1970-01-01
      • 2021-03-09
      • 2018-08-22
      相关资源
      最近更新 更多