39

Consider the following:

template<typename Der>
struct Base {
    // NOTE: if I replace the decltype(...) below with auto, code compiles
    decltype(&Der::operator()) getCallOperator() const {
        return &Der::operator();
    }
};

struct Foo : Base<Foo> {
    double operator()(int, int) const {
        return 0.0;
    }
};

int main() {
    Foo f;
    auto callOp = f.getCallOperator();
}

I want to create a member function in CRTP base class with a return type depending on signature of the operator() in the derived class. However decltype(&Der::operator()) fails to compile; The operator() member function in Foo is not visible. I assume that this is because the base class template is instantiated before Foo is fully defined.

Surprisingly, if I place auto for the return type it compiles. I assumed that auto would make the compiler deduce the return type from the function body and fail - because the body uses the not fully defined Foo type.

This behavior is the same for both MSVC 2015.3 and Clang 3.8

Why did the code start to work with auto? Does auto type deduction somehow "delay" the instantiation? Or use a different context than a hand-written return type expression?

4444
  • 3,523
  • 10
  • 27
  • 43
Michał W. Urbańczyk
  • 1,393
  • 11
  • 20
  • 5
    Good question. Upvoted. – Cheers and hth. - Alf Jul 12 '16 at 10:07
  • Possible duplicate of [CRTP and c++1y return type deduction](http://stackoverflow.com/questions/19892479/crtp-and-c1y-return-type-deduction) – Holt Jul 12 '16 at 11:19
  • Its the same issue but question and answer approach it from slightly different angle. Here we have "how the auto deduction is different", the linked question and its answer is more about "how can I make the hand-written expression work". Though it may still qualify as duplicate… – Michał W. Urbańczyk Jul 12 '16 at 11:42

1 Answers1

24

Your guess is correct. A deduced return type is not actually deduced until the signature of the function is needed. This means that it will be deduced in the context of the call to getCallOperator, at which point Foo is fully defined.

This is specified in 7.1.6.4p12:

Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.

Sebastian Redl
  • 61,331
  • 8
  • 105
  • 140