38

I have compiled following program in GCC using C++14.

#include <iostream>
using namespace std;

auto func(int i);

int main() 
{
    auto ret = func(5);
    return 0;
}

auto func(int i) 
{
  if (i == 1)
    return i;              
  else
    return func(i-1) + i;  
}

But, I get the following error.

In function 'int main()': 8:16: error: use of 'auto func(int)' before
deduction of 'auto'
 auto ret = func(5);

So, what am I missing here?

msc
  • 33,420
  • 29
  • 119
  • 214
  • 10
    Remember that C++ is designed for one-pass compilers (and many for it still are). The deduction for `ret` depends on `func`, but that cannot be available yet when preforming a single pass translation. – StoryTeller - Unslander Monica Apr 20 '17 at 08:59
  • What return type do you think should be given to `func` and why? – n. m. could be an AI Apr 20 '17 at 09:08
  • @n.m. return type int because func() return's sum of 1 to n numbers. – msc Apr 20 '17 at 09:11
  • 1
    Only now have I realized that the function in your question is identical to the one in the example in the standard... Is that a coincidence? Because if it isn't, then you've just missed the 2nd and 3rd lines of the example. (Yes, I do realize this is a pretty common recursion scheme. Just making sure.) – Griwes Apr 20 '17 at 09:15
  • Oops my mistake, please ignore. – n. m. could be an AI Apr 20 '17 at 09:23
  • @StoryTeller I'm not sure it's possible for fully-compliant compilers to be single-pass, per the discussion [here](http://stackoverflow.com/a/29662526/1858225). In any case, it certainly seems quite difficult. But you're absolutely right that much of C++ is designed *as if* this were the desired approach. – Kyle Strand Apr 20 '17 at 20:02

4 Answers4

46

This is [dcl.spec.auto/11]:

If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements. [ Example:

auto n = n;                     // error, n's type is unknown
auto f();
void g() { &f; }                // error, f's return type is unknown
auto sum(int i) {
  if (i == 1)
    return i;                   // sum's return type is int
  else
    return sum(i-1)+i;          // OK, sum's return type has been deduced
}

 — end example ]

To translate this into English: the compiler needs to know the return type before you can use the function. In case of auto used like this, this is typically achieved by moving the definition before the point of use. If you don't actually need to use return type deduction, you can keep the definition after the use if you provide a signature, including the return type, in the declaration.

Griwes
  • 8,805
  • 2
  • 43
  • 70
  • 6
    Gotta love them standard quotes. – StoryTeller - Unslander Monica Apr 20 '17 at 09:01
  • 1
    Straight to the point, great answer! I think it's also worth mentioning that even if in this case it looks like the compiler should be able to magically infer the return type even if `f` is defined after `main`, the problem really appears when the definition of `f` is in a separate translation unit: in that case, it's way too hard for the compiler to figure out what's going on. – vsoftco Apr 20 '17 at 09:41
35

Clang has a much better error message for that one:

main.cpp:8:16: error: function 'func' with deduced return type cannot be used before it is defined
    auto ret = func(5);
               ^

I guess that's self-explanatory.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
6

When auto is used as the return type in a function declaration that does not use the trailing return type syntax, the keyword auto indicates that the return type will be deduced from the operand of its return statement. That means the deduction can't be performed until the definition of the function func(), but before that it has been used in main().

You could move the definition before main(), or use trailing return type syntax to specify the return type on the declaration.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
0

In your example, there is really no reason that you could not just move the implementation of the function before main():

#include <iostream>
using namespace std;  // you should avoid this, too

auto func(int i)
{
  if (i == 1)
    return i;
  else
    return func(i-1) + i;
}

int main()
{
    auto ret = func(5);
    return 0;
}

Otherwise you just can't use the auto keyword. In particular, you can't use auto in a recursive function which does not return anything. You have to use void. And that applies to lambda functions. For example:

int main()
{
    auto f = [](int i)
    {
        // ... do something with `i` ...
        if(i > 0)
        {
            f(i - 1);   // <-- same error here
        }
    }

    auto ret(func(5));
    return 0;
}

The call f(i - 1) has a problem. To fix it you have to replace the auto by the actual type:

int main()
{
    typedef std::function<void(int)> func_t;
    func_t f = [](int i)
    {
    ...

If you really want a function which support varying return types you want to use a template anyway, not auto. This is really only to help you with less typing, not so much as a way to allow "any type".

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156