Main Content

Expensive std::function type definition

Definition of std::function type uses pass-by-value semantics for arguments that are expensive to copy

Since R2024a

Description

This defect occurs if you use pass-by-value semantics for an argument that is expensive to copy when defining an std::function type. For example:

std::function<bool(std::string)> myFunc;     
In this code, the std::function type myFunc stores a callable entity that accepts an std::string object by value. Because this type copies expensive std::string objects, Polyspace® reports a defect.

Polyspace considers an argument to be expensive if its size is greater than 2 * sizeof(void*).

Risk

When defining an std::function type, if you specify pass-by-value semantics, then the compiler copies the argument when a target is invoked using the std::function type. Consider this code:

std::function<bool(std::string)> myFunc; //Defect

myFunc = [](const std::string &s) {};

bool foo(myFunc myFunc) {
	return myFunc("Foo");
}
The definition of the type myFunc uses pass-by-value semantics. The compiler copies the string "Foo" when invoking myFunc("Foo") and then passes a reference to this copy to the underlying lambda [](const std::string &s) {}.

Copying an expensive argument and then passing a reference to the copy is unnecessary inefficient.

Fix

Use pass-by-reference semantics in the std::function type definition:

std::function<bool(const std::string&)> myFunc; //Fixed

myFunc = [](const std::string &s) {};

bool foo(myFunc myFunc) {
	return myFunc("Foo");
}

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

Examples

expand all

In this example, the std::function type EventHandler allows the class Button to execute a callable entity when a click is simulated. When main() callsbutton.click(), the compiler copies the string "Button clicked!" and then passes a reference to this copy to the function handleButtonClick.

#include <iostream>
#include <string>
#include <functional>

// Event Handler type using std::function
using EventHandler = std::function<void(const std::string)>;  //Defect

class Button {
private:
    EventHandler onClick;

public:
    void setOnClick(EventHandler handler) {
        onClick = handler;
    }

    void click() {
        if (onClick) {
            onClick("Button clicked!");
        }
    }
};

void handleButtonClick(const std::string& message) {
    std::cout << "Handling button click: " << message << std::endl;
}

int main() {
    Button button;

    // Registering the event handler
    button.setOnClick(handleButtonClick);

    // Simulating a button click
    button.click();

    return 0;
}

Copying a string and then passing a reference to the copied string is inefficient. Polyspace reports a defect.

Correction — Define std::function Type Using Pass-by-Reference Semantics

To fix this defect, modify the type definition of the std::function type EventHandler so that it uses pass-by-reference semantics.

#include <iostream>
#include <string>
#include <functional>

// Event Handler type using std::function
using EventHandler = std::function<void(const std::string&)>;  //No Defect

class Button {
private:
    EventHandler onClick;

public:
    void setOnClick(EventHandler handler) {
        onClick = handler;
    }

    void click() {
        if (onClick) {
            onClick("Button clicked!");
        }
    }
};

void handleButtonClick(const std::string& message) {
    std::cout << "Handling button click: " << message << std::endl;
}

int main() {
    Button button;

    // Registering the event handler
    button.setOnClick(handleButtonClick);

    // Simulating a button click
    button.click();

    return 0;
}

Result Information

Group: Performance
Language: C++
Default: Off
Command-Line Syntax: EXPENSIVE_STD_FUNCTION
Impact: Low

Version History

Introduced in R2024a