c++ - 原型(prototype)与 decltype 和 auto 不匹配

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

考虑以下类:

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<T>::_id) MyClass<T>::getId()
  • decltype (((MyClass<T>*)(void)0)->MyClass<T>::_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<T>::_id) MyClass<T>::getId()
  • decltype (((MyClass<T>*)this)->MyClass<T>::_id) MyClass<T>::getId()

g++ 5.3.0

最佳答案

根据标准草案N4582 §5.1.1/p13 一般 [expr.prim.general](强调我的):

An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

(13.1) — as part of a class member access (5.2.5) in which the object expression refers to the member’s class63 or a class derived from that class, or

(13.2) — to form a pointer to member (5.3.1), or

(13.3) — if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [Example:

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

— end example ]

63) This also applies when the object expression is an implicit (*this) (9.3.1).

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

For an expression e, the type denoted by decltype(e) is defined as follows:

(4.1) — if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

(4.2) — otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

(4.3) — otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

(4.4) — otherwise, decltype(e) is the type of e.

The operand of the decltype specifier is an unevaluated operand (Clause 5).

[Example:

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&

— end example ] [ Note: The rules for determining types involving decltype(auto) are specified in 7.1.6.4. — end note ]

因此,由于 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++ 接受。

关于c++ - 原型(prototype)与 decltype 和 auto 不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37185803/

相关文章:

c++ - 用最少的代码行替换c++中的一个sef字符

c++ - 如何让编译器为派生类选择方法的非模板版本?

c++ - 可变参数类型的部分模板特化和扩展到外部类型的可变参数包导致歧义

c++ - 在多个声明中使用 auto 时,为什么需要为所有变量初始化?

c++ - 如何更新这样的 map 结构?

c++ - 我如何比较在 C++ 中分配给#define 的 bool 值

c++ - 如何将命令行参数转换为 int?

c++ - 为什么必须在哪里放置 “template”和 “typename”关键字?

c++ - 通过引用将函数对象传递给标准算法

c++ - 通过覆盖 C 标准库调用来测量堆使用情况