Main Content

AUTOSAR C++14 Rule A8-4-4

Multiple output values from a function should be returned as a struct or tuple

Since R2020b

Description

Rule Definition

Multiple output values from a function should be returned as a struct or tuple.

Rationale

In a C++ function, the return statement can return only the value stored in a single variable. But the values stored in any number of additional variables in the caller's scope can be modified by the callee if you pass these values by reference, and then modify them in the body of the callee. For example, consider the function foo:

int foo(int x, int& y)
{
  int z;
  y = x*x;
  z = x*x*x;
  return z;
}

The function foo effectively returns two integer values: the square of the input parameter x (returned by reference) and the cube of the input parameter x (returned by copy by using the return statement). Simultaneously using both strategies to return multiple values results in a complicated function interface and can make your code less readable and maintainable. Instead, storing all return values in a single struct or tuple and returning it by using the return statement results in a simpler, more unified interface.

A return statement that has a struct or a tuple might require expensive copying from one memory location to another. Most compilers support return value optimization and can eliminate this expensive copy, resulting in executable code with little to no overhead associated with such returns.

To help you decide whether to use a struct or a tuple to return multiple values, consider:

  • If your return type represents an abstraction, it is preferable to use a struct because you can provide a custom name for each component of the abstract data type.

  • Tuples are easier to work with because a returned tuple can be conveniently processed by using std::tie at the call site. The std::tie method puts the tuple elements directly into existing local variables in the caller.

Note

This rule also applies to std::pair, which is a special kind of tuple that has exactly two elements.

Polyspace Implementation

The checker flags a function declaration that satisfies one of these two conditions:

  • The function has a nonvoid return type and at least one nonconstant reference parameter

  • The function has more than one nonconstant reference parameters

Usage notes and limitations:

  • The checker flags pure virtual functions that violate this rule. These functions are flagged because, for any implementation of a pure virtual function to be compliant with this rule, the interface of the pure virtual function itself must obey this rule.

  • The checker does not flag operators that violate this rule.

Troubleshooting

If you expect a rule violation but Polyspace® does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

#include <tuple>


int Divide1(int dividend, // Noncompliant, remainder returned as reference parameter 
			int divisor, int& remainder)   
{
  remainder = dividend % divisor;
  return dividend / divisor;
}

// Compliant, quotient and remainder combined into a tuple
std::tuple<int, int> Divide2(int dividend, int divisor)  
{
  return std::make_tuple(dividend / divisor, dividend % divisor);
}

int main()
{
  int quotient, remainder;
  // store in local variables
  std::tie(quotient, remainder) = Divide2(26, 5); 
  return 0;
}

The function Divide1 has the quotient as the return value and the remainder as a nonconstant reference parameter. Having a nonvoid return value and a nonconstant reference parameter violates this coding rule.

The function Divide2 combines the quotient and the remainder into a tuple and returns the tuple. This code pattern complies with the rule.

struct fraction {
  int quotient;
  int remainder;
} ;



int Divide1(int dividend, // Noncompliant, quotient and remainder returned as reference parameters
			int divisor, int& quotient, int& remainder)   
{
  quotient = dividend / divisor;
  remainder = dividend % divisor;
}

// Compliant, quotient and remainder combined into a struct
fraction Divide2(int dividend, int divisor)  
{
  fraction answer;
  answer.quotient = dividend / divisor;
  answer.remainder = dividend % divisor;
  return answer;
}

int main()
{
  fraction answer;
  answer = Divide2(26,5);
  return 0;
}

The function Divide1 has both the quotient and the remainder as nonconstant reference parameters. Having multiple nonconstant reference parameters violates this coding rule

The function Divide2 combines the quotient and the remainder into a struct and returns the struct. This code pattern complies with the rule.

Check Information

Group: Declarators
Category: Advisory, Automated

Version History

Introduced in R2020b