Omit return type in C++11

16,373

Solution 1

It would appear that g++ 4.8 is getting an implementation of auto return type deduction. The patch was put in by Jason Merrill who is also sending a paper for C++-1Y for the feature. The feature is available with -std=c++1y.

Still playing with it.

Solution 2

The rationale for this behavior is given in the draft, 8.3.5p12:

A trailing-return-type is most useful for a type that would be more complicated to specify before the declarator-id:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

rather than

template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);

So this is really only meant to simplify the case where referring to the parameter names helps.

If you assume that C++ could always infer the return type of functions from the function body: this is not going to fly. It's a goal of C++ (and C) to allow modularity by separating declaration from implementation, so at the point of the call, you may not have the body of the function available. However, every caller needs to know the parameter types and the return type of every function/method being called.

Solution 3

If you are simply trying to set the return type, make it a template argument. This way you can change everything related to the return type without actually changing the function. You can put a default return type if you want like in this example.

template <class R = int, class T>
R f(T&& x)
{
   ...
   return h2(y2, y3);
}

The code below demonstrates it's effectiveness.

DEMO CODE:

#include <iostream>
#include <iomanip>

template <class T, class S>
T h2(T& x, S& y)
{
  return x + y;
}

template <class R = int, class T>
R f(T& x)
{
  auto y2 = x;
  auto y3 = x;
  return h2(y2, y3);
}

int main(int argc, char** argv)
{
  int x = 7;
  std::string str = "test! ";

  auto d = f<double>(x);
  auto i = f(x); // use default type (int)
  auto s = f<std::string>(str);

  std::cout << std::fixed << std::setprecision(4);
  std::cout << "double: " << d << std::endl;
  std::cout << "int: " << i << std::endl;
  std::cout << "string: " << s << std::endl;

  return 0;
}

OUTPUT:

double: 14.0000
int: 14
string: test! test!

Unfortunately, the exact functionality you are looking for does not exist (yet) and is not part of the C++0x spec. However, it is possible this may be part of the C++1x spec when it is drafted. until then, stick to templates.

Solution 4

EDIT: oops, I just realized that there's a scoping difference between the trailing-return-type specifier and the return statement. Specifically:

auto f(int a)
{
    char r[sizeof(f(a))+1];
    return r;
}

Kaboom!


Previous answer:

It's unfortunate that the language does not provide a syntax to have the compiler infer the return type in this case, because it's trivial to show that inference is possible.

Specifically, we are talking about the case where there is exactly one return statement inside the function.

Independent of where in the function that return statement is, or how complex the preceding code is, it should be clear that the following transformation is possible:

return (ugly expression);

into

auto return_value = (ugly expression);
return return_value;

If the compiler can infer the type of return_value (and according to the C++0x rules, it can), then the inferred type of return_value can be chosen as the return type of the function.

It therefore seems to me that a modification to C++0x where the trailing return type specifier should only be required when the multiplicity of return statements is not exactly one would be feasible and solve the problem.

Solution 5

I agree with Yttrill. Return type deduction has already been proved to be a feasible practice in languages like Haskell, and since C++ has already achieved 'auto', it is just one step further to achieve return type deduction. This deduction should happens at the time of specialization, not template definition, since information of the real type supplied to the template is needed. The separate of declaration and definition is no longer a common practice in generic C++, because template body must be written in header files, and hence template bodies almost always go with template declarations. In situations where there are multiple return statements and types of them do not match, the compiler can happily report en error. In summary, return type deduction is totally possible in C++, if the committee wants to. And it's VERY important, because the duplication of manually writing return types hinders the pervasive use of small generic helper functions, which is such a common practice in functional and generic programming.

Share:
16,373
Clinton
Author by

Clinton

Updated on July 05, 2022

Comments

  • Clinton
    Clinton almost 2 years

    I've recently found myself using the following macro with gcc 4.5 in C++11 mode:

    #define RETURN(x) -> decltype(x) { return x; }
    

    And writing functions like this:

    template <class T>
    auto f(T&& x) RETURN (( g(h(std::forward<T>(x))) ))
    

    I've been doing this to avoid the inconvenience having to effectively write the function body twice, and having keep changes in the body and the return type in sync (which in my opinion is a disaster waiting to happen).

    The problem is that this technique only works on one line functions. So when I have something like this (convoluted example):

    template <class T>
    auto f(T&& x) -> ...
    {
       auto y1 = f(x);
       auto y2 = h(y1, g1(x));
       auto y3 = h(y1, g2(x));
       if (y1) { ++y3; }
       return h2(y2, y3);
    }
    

    Then I have to put something horrible in the return type.

    Furthermore, whenever I update the function, I'll need to change the return type, and if I don't change it correctly, I'll get a compile error if I'm lucky, or a runtime bug in the worse case. Having to copy and paste changes to two locations and keep them in sync I feel is not good practice.

    And I can't think of a situation where I'd want an implicit cast on return instead of an explicit cast.

    Surely there is a way to ask the compiler to deduce this information. What is the point of the compiler keeping it a secret? I thought C++11 was designed so such duplication would not be required.

    • SoapBox
      SoapBox over 13 years
      Maybe you need to reconsider the code you're writing if you have so many functions using decltype. We survived a long time without decltype at all...
    • GManNickG
      GManNickG over 13 years
      (You make it sound like you're changing the function every other hour.) Yes, it would be nice if the compiler could deduce the return type automatically, but it simply isn't there, and there's a point where you have to stop trying to be the compiler and just write code it accepts. (And yes, C++0x alleviates many things, but it can't, unfortunately, go all out.) So just specify the return type.
    • Clinton
      Clinton over 13 years
      I don't think all the negative comments are reasonable. Boost libraries are full of function objects with things like ::return_type to work around these sort of issues. Do you really think that having to define a new class just to define the return type of your function is 'readable'?! I don't see what is so unreadable about not having to replicate the same information in three different ways.
    • Yakov Galka
      Yakov Galka over 13 years
      @Clinton: I think that if you can't easily reason about what's the return type of your function, then your design is wrong.
    • Clinton
      Clinton over 13 years
      @ybungalobill: Can you easily reason about the return type of boost fusion vectors? If not, do you think their design is wrong? And if so, how would you do it?
    • Yakov Galka
      Yakov Galka over 13 years
      @Clinton: I don't understand your question, boost fusion vectors don't have an invocation operator, so what return type??..
    • Puppy
      Puppy over 13 years
      There's a reason that decltype and the lazy return type syntax was introduced, and that's because it's impossible or unbelievably hard to determine the type of many functions involving forwarding beforehand. @SoapBox: I disagree wholeheartedly in a respectful manner.
    • Faisal Vali
      Faisal Vali over 12 years
      @Clinton - This is a very reasonable request especially since lambda return types are deduceable in certain situations - I believe deduceable return types for all functions in similar situations is still being pondered by the standard's committee. It would certainly help if a compiler implemented such an extension successfully first.
    • alfC
      alfC about 11 years
      It has some limitations but this can be useful cpp-next.com/archive/2011/11/having-it-all-pythy-syntax and github.com/pfultz2/Pythy
    • cheshirekow
      cheshirekow over 10 years
      @LokiAstari I agree with your comment in the context of the OP but what if a function returns the result of std::bind(...)?
    • Martin York
      Martin York over 10 years
      @cheshirekow: That is the perfect place (with std::bind) to use auto. With std::bind you don't actually care about the actual underlying type; just that it will perform the actions you want.
  • Clinton
    Clinton over 13 years
    the return type would be something like decltype(h2(h(f(x),g1(x),h(f(x),g2(x))). I could write it explicitly, but it is messy repetition.
  • user253751
    user253751 over 13 years
    because you can't figure out the type ahead of time? eg: if h2 returns an X, set it to X
  • Puppy
    Puppy over 13 years
    With rvalue references, that's often not possible. The entire reason this syntax was devised is because it's not possible to know the return type ahead of time.
  • user253751
    user253751 over 13 years
    but... why are you changing the return type every time you edit the function? "Furthermore, whenever I update the function, I'll need to change the return type"
  • Clinton
    Clinton over 13 years
    Yttrill: Could you elaborate on this? Would "template class<T> decltype(a.f()) f(T a) { return a.f(); } fail? (which is what my macro does)
  • Martin v. Löwis
    Martin v. Löwis over 13 years
    what you are missing that "auto" for return types means that you need to use the trailing-return-type declaration (-> T).
  • Ben Voigt
    Ben Voigt over 13 years
    No, you can't figure out the type ahead of time. It depends on T. Remember that g1(), g2(), h(), and h2() can all be template functions and can be specialized. Adding a new specialization to any of those other functions could change the return type of f() and would do so without any modifications to f() itself
  • Alex B
    Alex B about 13 years
    If you have multiple return statements with different types, you need to find a superset of all types specified. I think this feature will be needlessly complicated and result in a lot of cryptic errors and corner cases: (e.g. you return pointers A* and B* to forward declared classes, which are actually derive from on another, if only forward declarations are available, deduction will fail).
  • Yoav
    Yoav over 11 years
    For many years people even did have C++! Why do we need C++, you can always use C, or even assembly. Code duplication is known as the best programming practice ever!
  • Charphacy
    Charphacy about 11 years
    But you can write auto f = [&](int a) { char r[sizeof(f(a))+1]; return r; };, and compilers just give you an error if you do, e.g., “error: variable 'f' declared with 'auto' type cannot appear in its own initializer”
  • emsr
    emsr about 11 years
    Return type deduction has been voted into C++14. The latest paper has the details.
  • EFraim
    EFraim over 10 years
    Template bodies do not really have to be written in header files: you can write the body in .cpp .cxx as usual and have it explicitly instantiated for instances that you can use. This does not work for all use-cases, but it often does help reduce compilation time and improve readability.
  • j_kubik
    j_kubik over 9 years
    (-1) Independent of your reasoning, it is rather clear that OP knew about this possibility, and it doesn't solve his problem. And as for your question - "Does your return type really change that often?" - well, if it is just some helper inline function (for which auto is mostly useful for) it will be necessary to get return type exactly as result of the expression producing it. If it differs, compiler will be forced to do additional type conversion even in inlined code, which is usually not what we want - and even if it doesn't actually lose data, it is suboptimal performance-wise.
  • kirbyfan64sos
    kirbyfan64sos about 9 years
    In C++14, this is possible.