Contenuto principale

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:

#include <string>
#include <functional>

using StringConsumer = std::function<void(std::string)>; // takes by value 

void InvokeConsumer(StringConsumer consumer, const std::string& inputText)
{
    // std::function<void(std::string)> forces a copy here
    consumer(inputText);
}

void main(const std::string& message)
{
    InvokeConsumer([](const std::string &text){}, message);
}
The definition of the type StringConsumer uses pass-by-value semantics. The compiler copies the string variable inputText when invoking consumer(inputText) and then passes a reference to this copy to the underlying lambda [](const std::string &text) {}.

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

Fix

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

#include <string>
#include <functional>

using StringConsumer = std::function<void(const std::string&)>; // takes by const-ref 

void InvokeConsumer(StringConsumer consumer, const std::string& inputText)
{
    // std::function<void(const std::string&)> does not copy 
    consumer(inputText);
}

void main(const std::string& message)
{
    InvokeConsumer([](const std::string &text){}, message);
}

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() calls button.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
PQL Name: std.defects.EXPENSIVE_STD_FUNCTION

Version History

Introduced in R2024a