原型与 decltype 和 auto 不匹配
prototype mismatch with decltype and auto
考虑以下 class:
class MyClass
{
int _id;
public:
decltype(_id) getId();
};
decltype(MyClass::_id) MyClass::getId()
{
return _id;
}
编译正常。
然而,当我用它制作模板 class 时:
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)
我可以通过在 class:
中定义 body 来修复它
template <class T>
class MyClass
{
int _id;
public:
decltype(_id) getId() { return _id; }
};
尾随 return 类型遇到类似的问题:
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
这似乎是 g++
错误。
我在 Visual Studio 2015 年尝试过您的代码:
Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped
编辑:我发现 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 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);
}
根据标准草案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](强调我的):
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++接受。
考虑以下 class:
class MyClass
{
int _id;
public:
decltype(_id) getId();
};
decltype(MyClass::_id) MyClass::getId()
{
return _id;
}
编译正常。
然而,当我用它制作模板 class 时:
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)
我可以通过在 class:
中定义 body 来修复它template <class T>
class MyClass
{
int _id;
public:
decltype(_id) getId() { return _id; }
};
尾随 return 类型遇到类似的问题:
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
这似乎是 g++
错误。
我在 Visual Studio 2015 年尝试过您的代码:
Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped
编辑:我发现 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 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);
}
根据标准草案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](强调我的):
For an expression
e
, the type denoted bydecltype(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)
isT&&
, whereT
is the type ofe
;(4.3) — otherwise, if e is an lvalue,
decltype(e)
isT&
, whereT
is the type ofe
;(4.4) — otherwise,
decltype(e)
is the type ofe
.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++接受。