12

I'm attempting to solve a problem in which decltype will greatly simplify things, but I'm running into an issue using decltype on *this and adding a const qualifier. The sample code below demonstrates the problem.

#include <iostream>

struct Foo
{
  void bar()
  {
    static_cast<const decltype(*this)&>(*this).bar();
  }

  void bar() const
  {
    std::cout << "bar" << std::endl;
  }
};

int main(int argc, char* argv[])
{
  Foo f;
  f.bar(); // calls non-const method
  return 0;
}

The code compiles in MSVC2010, but execution recurses until a stack overflow occurs.

Ideone reports compiler error

prog.cpp: In member function 'void Foo::bar()':
prog.cpp:7:38: error: 'const' qualifiers cannot be applied to 'Foo&'

If I change the line

static_cast<const decltype(*this)&>(*this).bar();

to

static_cast<const Foo&>(*this).bar();

it works as expected.

Am I misusing or misunderstanding decltype?

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
Jesse Stimpson
  • 972
  • 9
  • 19
  • 2
    `const_cast` may express the intent a bit better. – Cubbi Sep 14 '11 at 14:49
  • @Cubbi Express the intent to whom? – Jesse Stimpson Sep 14 '11 at 17:22
  • @Jesse : I presume he means `const_cast(this)->bar();` is easier to _read_ than anything involving `decltype` for this scenario. – ildjarn Sep 14 '11 at 17:39
  • @ildjam I meant to suggest using const_cast to show that all that complicated expression does is alter constness, but [looks like I was wrong](http://stackoverflow.com/questions/3402318/const-cast-vs-static-cast). – Cubbi Sep 14 '11 at 18:30
  • Note that the type of `*this` can always be specified by the injected-class-name, even inside a template. `decltype( *this )` could only be useful in a preprocessor macro. – Potatoswatter Sep 15 '11 at 01:37
  • 1
    @Potatoswatter - You caught me. ;) I am indeed using `decltype` in a preprocessor macro to "generate" some code for a certain idiom involving const correctness. I realize that the preprocessor is not the ideal code generation tool. – Jesse Stimpson Sep 15 '11 at 11:54

1 Answers1

14

Since the expression *this is not an id-expression (i.e. it doesn't name an entity, like a variable), then decltype(*this) gives the type of the expression *this. That type is Foo&, so adding a const qualifier and making a reference to that doesn't change anything: either it silently collapse to Foo& (following rules like reference collapsing), or it's an error (a const reference type). I'm not sure which behaviour is correct, and you have in fact found two compilers which behave differently. In any case it doesn't matter because it's not what you want.

You can use std::remove_reference<decltype(*this)>::type const& instead but that looks a bit ugly.

In case you're still confused:

int* p;
// decltype(p) is the type of the variable p (or, the declared type)
// int*

// decltype( (p) ) is the type of the expression p
// int*& because p is an lvalue

// decltype(*p) is the type of the expression *p
// int& because *p is an lvalue
Luc Danton
  • 34,649
  • 6
  • 70
  • 114