Main Content

Expensive pass by value

Parameter might be expensive to copy

Since R2020b

Description

This defect occurs when you pass a parameter by value instead of by reference or pointer, but the parameter is unmodified and either:

  • The parameter is a non-trivially copyable type. For more on non-trivially copyable types, see is_trivially_copyable.

  • The parameter is a trivially copyable type that is above a certain size. For example, an object greater than 2 * sizeof(void *) is more expensive to pass by value than by reference or pointer.

Polyspace® flags unmodified parameters that meet the preceding conditions even if the parameters are not declared const.

Polyspace raises no defect if:

  • The passed by value parameter is a move-only type. For instance, std::unique_ptr can be moved-from but cannot be copied.

  • The passed by value parameter is modified.

  • The parameter is passed by value to a virtual overriding method in a derived class.

    In this instance, fixing the defect might require modifying the signature of the base class method which might not be possible for the owner of the derived class.

  • The parameter is passed by value to a virtual method in a base class and the parameter is non-const.

    In this instance, if there are overriding methods in derived classes that modify their parameter, it might not be possible to properly update those methods if a fix is applied to the base class method.

    Note that if the parameter is const, overriding methods in derived classes do not modify their parameters and applying a fix in the base class is safe. In this case, Polyspace reports an expensive pass by value defect.

For example, in the following code, Polyspace does not report a defect in these functions and methods:

  • The function offset() because its parameter is modified.

  • The function moveOnly() because its parameter is a move-only type.

  • Methods D::parse_M and D::print_M because they are virtual overriding methods in a derived class.

  • The method Base::print_M because it is a virtual method in a base class with a non-const parameter.

Polyspace does report a defect for Base::parse_M because its parameter is const and non-trivially copyable.

#include <string>
#include <memory>
#include <iostream>

typedef struct Buffer
{
    unsigned char bytes[20];
    int index;
} Buffer;

void offset(Buffer modifiedBuffer)
{
    ++modifiedBuffer.index;
}

void moveOnly(std::unique_ptr<Buffer> move_only_param);

// virtual methods example

class Base
{
public:
    virtual void parse_M(const std::string msg);
    virtual void print_M(std::string msg)
    {
        std::cout << "Base message: " << msg << "\n";
    }
};

class D : public Base
{
public:
    void parse_M(const std::string msg) override;
    void print_M(std::string msg) override
    {
        std::cout << "Derived message: " << msg << "\n";
    }
};

Risk

Passing a parameter by value creates a copy of the parameter which is inefficient if the parameter is expensive to copy. Even if your intent is to pass by reference or pointer, you might forget the const& or const* in your function signature and inadvertently run an inefficient version of the function.

Fix

Convert the parameter to a const pointer (const*) for C code, or to a const reference (const&) for C++ code.

Performance improvements might vary based on the compiler, library implementation, and environment that you are using.

Examples

expand all

#include<string>

class Player
{
public:
    void setName(std::string const str)
    {
        name = str;
    }
    void setRank(size_t r)
    {
        rank = r;
    }
    // getter functions implementation
private:
    std::string name;
    size_t rank;

};

In this example, Polyspace flags the parameter of setter function setName which is passed by value and results in an expensive copy. The type std::string is not trivially copyable. The passed by value parameter of setRank is not flagged because size_t is a small trivially copyable type.

Correction — Pass std::string Parameter by const Reference

To avoid an inefficient copy operation, use a const& to pass the parameter.

#include<string>

class Player
{
public:
    void set_name(std::string const& s)
    {
        name = s;
    }
    void set_rank(size_t r)
    {
        rank = r;
    }
    // getter functions implementation
private:
    std::string name;
    size_t rank;

};
#include<stdio.h>
#include<string.h>

typedef struct _Player {
    char name[50];
    size_t rank;
} Player;


void printPlayer(Player const player)
{

    printf("Player name: %s\n", player.name);
    printf("Player rank: %zu\n", player.rank);
}

In this example, Polyspace flags the parameter of printPlayer which is passed by value and results in an expensive copy.

Correction — Pass Large Struct by const Pointer

To avoid an inefficient copy operation, use a const* to pass the parameter, then use the appropriate notation to read the structure elements.

#include<stdio.h>
#include<string.h>

typedef struct _Player {
    char name[50];
    size_t rank;
} Player;


void printPlayer(Player const* player)
{

    printf("Player name: %s\n", player->name);
    printf("Player rank: %zu\n", player->rank);
}

Result Information

Group: Performance
Language: C | C++
Default: Off
Command-Line Syntax: EXPENSIVE_PASS_BY_VALUE
Impact: Medium

Version History

Introduced in R2020b

expand all