Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding value category of functions in C++

Tags:

c++

First of all, I have read through this article, explaining the functionalities of std::move and std::forward. In that post, there is an example code snippet as follows which I use in my test.

std::map<std::string, std::function<void()>> commands;

template<typename ftor>
void install_command(std::string name, ftor && handler)
{
  commands.insert({
    std::move(name),
    std::forward<ftor>(handler)
  });
}

As I want to experiment on the actual usage, I have written a simple piece of code as shown below.

#include <iostream>
#include <map>
#include <functional>

using namespace std;

// code copied from above goes here

void fnA() { cout << "Function A." << endl; }
function<void()> fnB = [&]() -> void {
    cout << "Function B." << endl;
}

void RunTest() {
    install_command("#1", fnA);
    install_command("#2", move(fnA));
    install_command("#3", fnB);
    //install_command("#4", move(fnB));

    fnA();
    fnB();

    for (const auto& p : commands) {
        cout << p.first.c_str() << ": " << &p.second << endl;
    }
}

int main() {
    RunTest();
    return 0;
}

The program is compiled with -std=c++11, and the execution result is as follows.

Function A.
Function B.
#1: 0xaf8088
#2: 0xaf8018
#3: 0xaf81a8

If I uncomment the line install_command("#4", move(fnB));, there is a runtime error.

terminate called after throwing an instance of 'std::bad_function_call'
  what():  bad_function_call
Function A.

I believe the ownership of the lambda function has been transferred from function<void()> fnB to commands["#4"], but why does fnA() work while fnB() does not?

like image 728
S.C. Avatar asked Nov 24 '25 21:11

S.C.


1 Answers

fnA is a regular function not a std::function as fnB

When you do

commands.insert({
    std::move(name),
    std::forward<ftor>(handler)
})

You create

  • std::function<void()> from void(&)() for fnA
  • std::function<void()> from void(&)() for std::move(fnA)
  • std::function<void()> from std::function<void()>& for fnB (so copy constructor)
  • std::function<void()> from std::function<void()>&& for std::move(fnB) (so move constructor)

Only the later modify input argument.

like image 163
Jarod42 Avatar answered Nov 27 '25 13:11

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!