0

I have the following class with a method signature as below:

class Foo
{
    public:
        std::vector<std::string> barResults(const std::vector<std::string>&, const std::vector<std::string>&);
}

In the implementation file, I've got this:

std::vector<std::string> Foo::barResults(const std::vector<std::string>& list1, const std::vector<std::string>& list2)
{
    std::vector<std::string> results;
    // small amount of implementation here...
    return results;
}

So I thought to myself, let's see if I can simplify this function signature a bit with some auto-magic as it's getting to be a "bit of a line-full"! So I tried this...

class Foo
{
    public:
        auto barResults(const std::vector<std::string>&, const std::vector<std::string>&);
}

auto Foo::barResults(const std::vector<std::string>& list1, const std::vector<std::string>& list2)
{
    std::vector<std::string> results;
    // small amount of implementation here...
    return results;
}

Now ignoring the fact that, yes I can use a "using namespace std", to trim it down a lot, I was wondering why the compiler gave me an error "a function that returns 'auto' cannot be used before it is defined".

I personally would have thought the compiler would have easily been able to deduce the return type of the method, but in this case it doesn't seem so. Sure, you can fix it with a trailing return type as below:

class Foo
{
    public:
        std::vector<std::string> barResults(const std::vector<std::string>&, const std::vector<std::string>&) -> std::vector<std::string>;
}

But then if you use the above, it's no better than it was before. So, apart from "using namespace std", is there a nicer way to do the above, and why can't the compiler deduce the return-type in this instance? Or even, does it depend on how this method is invoked that's causing the compiler not to be able to figure out the return type.

The Welder
  • 916
  • 6
  • 24

3 Answers3

5

The issue here is an issue of how include files work. Your error:

a function that returns 'auto' cannot be used before it is defined

means that in the file you are using your function, its definition (ie. implementation) not anywhere in the file before the usage. This means that the compiler compiling your code using the function can't deduce the functions return type, as that requires access to the definition (implementation). The most likely reason for this is that the function's definition (implementation) is in its own source (.cpp, .c, etc.) file, that is not included. To more fully understand this, I recommend reading this answer, and perhaps this answer as well.


To address the titular question, likely the easiest way to shorten that signature is with a typedef. More specifically, you can add the following code wherever you see appropriate, provided the scoping is appropriate (I would add it as a public member in your class):

typedef std::vector<std::string> strvec;

This allows you to re-write your method signature as the much more manageable:

strvec barreuslts(const strvec&, const strvec&)
john01dav
  • 1,842
  • 1
  • 21
  • 40
  • As I said above, using a typedef or using was something I'd already considered and I really only wanted to shorten the line length a little without changing the entire signature. However, the other point you made is something that I hadn't thought about. Since I only include the header and it's the implementation that is required for type deduction, that's the reason why I got the error. Nice. – The Welder Aug 15 '18 at 11:42
2

When sticking to C++11, you can't rely on deduced return types, you need the trailing return type (C++14 allows that, though). As there is nothing special about the trailing return type in your case, i.e., no expression is passed decltype to determine the return type, I would instead try to shorten the method signature with some type aliases:

class Foo
{
    public:
        using StrVec = std::vector<std::string>;

        StrVec barResults(const StrVec&, const StrVec&);
};

Foo::StrVec Foo::barResults(const StrVec& list1, const StrVec& list2)
{
    StrVec results;
    // small amount of implementation here...
    return results;
}
lubgr
  • 37,368
  • 3
  • 66
  • 117
  • This is an idea I'd already thought about anyway, but this code was for some junior coders and didn't want to complicate things too much by using something like a typedef or using. It's a good approach though, to be sure. – The Welder Aug 15 '18 at 11:39
  • I doubt that introducing auto return will reduce the complication, while using an in-class typedef with a sensible name can make the code a lot more readable. By the way I would use the c++11 `-> StrVec` declaration of the trailing return type syntax to get rid of that pesky Foo:: at the start, though in the end it costs 2 extra characters for Foo! – Gem Taylor Aug 15 '18 at 15:35
2

If you are just looking for a visually appealing way to deal with longer signatures, stop forcing everything to be on one line. Be liberal with your vertical spacing and insert line breaks. The signature contains quality information for the caller and what you may be looking for is a multi-line signature. If you are working with 80 character page widths, reconsider the indentation on the access specifier.

class Foo
{
public:
    std::vector<std::string> barResults(const std::vector<std::string>&,
                                        const std::vector<std::string>&);
}


std::vector<std::string>
Foo::barResults(const std::vector<std::string>& list1,
                const std::vector<std::string>& list2)
{
    std::vector<std::string> results;
    // small amount of implementation here...
    return results;
}

There are many styles when it comes to splitting up a declaration. Having a tool like clang-format in your toolset will do this automatically and consistently for you.

Snowhawk
  • 679
  • 7
  • 9
  • I usually am liberal with spaces, but this is more of a compound question about both formatting and what caused the error. I'm also *NOT* a fan of using auto everywhere, as like you, I agree that a signature contains quality information. It was just a case that in this instance, the signature was rather verbose and when I wanted to shorten it, just a little, with a small use of auto, it generated an error I wasn't expecting. Now that someone pointed it out, it makes perfect sense as to why it happened. – The Welder Aug 15 '18 at 11:46