Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

prototype mismatch with decltype and auto

Consider the following class:

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

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

It compiles fine.

However when I make a template class out of it:

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

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

I get:

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();
                   ^

Why is that?
Why the different types

  • decltype (MyClass<T>::_id) MyClass<T>::getId()
  • decltype (((MyClass<T>*)(void)0)->MyClass<T>::_id)

I could fix it by defining the body in the class:

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

Trailing return type suffers a similar problem:

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

error:

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

like image 206
otisonoza Avatar asked May 12 '16 11:05

otisonoza


People also ask

What is the difference between auto and decltype Auto?

'auto' lets you declare a variable with a particular type whereas decltype lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression.

What does decltype auto do?

Use auto and decltype to declare a function template whose return type depends on the types of its template arguments. Or, use auto and decltype to declare a function template that wraps a call to another function, and then returns the return type of the wrapped function.

How do I use decltype in CPP?

In C++11, you can use the decltype type specifier on a trailing return type, together with the auto keyword, to declare a template function whose return type depends on the types of its template arguments.


2 Answers

According to the draft standard N4582 §5.1.1/p13 General [expr.prim.general] (Emphasis Mine):

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).

Also from §7.1.6.2/p4 Simple type specifiers [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 ]

Consequently, since decltype is an unevaluated operand the code is legitimate and should compile.

One clean workaround would be to use decltype(auto):

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

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

Above code is accepted by GCC/CLANG/VC++.

like image 108
101010 Avatar answered Oct 26 '22 22:10

101010


It seems like this is g++ bug.

I've tried your code in Visual Studio 2015:

Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped

Edit: I found 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
}

Output:

0
like image 31
Dmitriy Zapevalov Avatar answered Oct 26 '22 23:10

Dmitriy Zapevalov