Несоответствие прототипа с помощью decltype и auto
Рассмотрим следующий класс:
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
Ответы
Ответ 1
В соответствии с проектом стандарта N4582 §5.1.1/p13 Общие сведения [expr.prim.general] (Акцент Mine):
Идентификатор, который обозначает нестатический элемент данных или нестатический функция члена класса может использоваться только:
(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] (Акцент Mine):
Для выражения e
тип, обозначенный символом decltype(e)
, определяется как следующим образом:
(4.1) - если e
- это неравномерное id-выражение или unparenthesized член класса (5.2.5), decltype(e)
- тип объекта названный e. Если такой объект отсутствует или если e называет набор перегруженные функции, программа плохо сформирована;
(4.2) - в противном случае, если e - значение x, decltype(e)
- T&&
, где T
- тип e
;
(4.3) - в противном случае, если e является l значением, decltype(e)
является T&
, где T
является типом e
;
(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/VС++.
Ответ 2
Кажется, что это ошибка g++
.
Я пробовал свой код в Visual Studio 2015:
Сборка: 1 успешно, 0 не удалось, 0 обновлено, 0 пропущено
Изменить: я нашел обходной путь:
#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
Ответ 3
Кажется, что GCC Ошибка 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);
}