Function Objects in C++
An object of a class that defines operator() can be called too.
A Variable That Accepts Many Kinds of Callables
In C++ several different things can be called with the call operator (). A plain function can be called. A lambda can be called. An object of a class that defines operator() can be called too. Each of these is written with its own syntax, so the three look quite different on the page. Yet a programmer often wants one variable that can hold any of them and be called in the same way. The standard library provides exactly this kind of type under the name function, written std::function in full.
What do you think happens?
Suppose a std::function is declared to take a double argument, and you try to assign a function that takes an int argument to it. Will the assignment be accepted?
Reveal answer
No, because the argument types do not match
The declared type fixes the argument type as double. A callable whose argument type is int has a different call signature, so the assignment is rejected. The rest of this article explains the rule behind this result.
Reading the Declaration Piece by Piece
A std::function is specified with a specific return type and a specific argument type. The source gives the example function<int(double)>. Read it as a pattern: the type inside the angle brackets begins with the return type, followed by the argument type in parentheses. In function<int(double)> the return type is int and the argument type is double. This fixed pair is written into the declared type itself, so the variable's type says what kind of callable it is willing to hold.
The declaration can also create the variable and store a callable in it at the same moment. The source gives the form function<int(double)> fct {f1}; which declares the variable fct and stores f1 in it during the declaration. The variable can later be given a different callable of the same signature by assignment.
- The declaration fixes the return type int and the argument type double
- Initialization stores f1 in fct at the moment of declaration
- Assignment later replaces the stored callable, but only with one that fits the same signature
What a Stored Callable Has to Match
The source makes a central point here. Ordinary functions, lambdas, and function objects can all be stored in a std::function when their signatures match. The variable does not care how the callable was written. It checks only the call signature: how many arguments it takes, what their types are, and what type of result it produces. This makes the declared type a contract about arguments and return values, not about the internal form of the callable.
The question to carry forward is this: what property of the declared type decides whether a given assignment is accepted? The answer is the signature. A callable is accepted when its argument types and return type fit the pair declared inside the angle brackets.
Tracing the Assignments in the Source Example
The source shows a declaration and then a series of assignments to the same variable. Read the code first, noting which assignments are expected to succeed and which one is marked as an error. In the first two assignments, one callable is a lambda and the other is a named function. Both are accepted, even though they were written in different ways.
Why the third assignment fails
fct was declared as std::function<int(double)>. Assigning f2 to fct is rejected. Why?
- 1Identify the declared signature
fct was declared with argument type double and return type int.
- 2Identify the callable's argument type
f2 has the argument type int, which differs from double.
- 3Compare the signatures
The argument types do not match, so f2 does not fit the contract of fct.
- 4Read the reason from the source
The source's comment states directly that the argument type is incorrect, and the assignment is flagged as an error.
The assignment fct = f2; is rejected at compile time because the argument type of f2 does not match the declared argument type double.
| Assignment | Callable kind | Signature matches int(double)? | Result |
|---|---|---|---|
| First assignment | Lambda or named function (per the source) | Yes | Accepted |
| Second assignment | The other of lambda or named function | Yes | Accepted |
| fct = f2; | Function whose argument type is int | No | Rejected |
Which assignments succeed when fct is declared as std::function<int(double)>
Function Objects Compared With Plain Functions and Lambdas
A function object is a class object that defines operator(), so that the object itself can be called with the call operator. It is one of the three kinds of callable the source names. The three can be told apart by how each is written. A plain function is declared with a name and a body. A lambda is a callable written inline in the code. A function object is an object whose class defines operator(). All three can be stored in a std::function whose signature they match.
result is 5- Doubler is a class whose objects can be called because it defines
operator() - Doubler{} creates a function object that is stored in fct
- The call
fct(2.5)goes through the stored callable'soperator()
Mistakes With Signatures
Common mistake Assuming that a std::function accepts any callable with the right number of arguments
What it looks likestd::function<int(double)> fct; then assigning a function that takes an int argument
Why it failsThe source states that the argument type must match. An int parameter where double is declared cannot be assigned.
Do this insteadMake the callable's argument and return types match the signature written in the declaration, or declare the std::function with the type you actually need.
Common mistake Thinking that a lambda and a named function must be written the same way to be stored
What it looks likeExpecting a lambda to be rejected because it is not a named function
Why it failsThe source says the variable does not care how the callable was written. Only the call signature is checked.
Do this insteadCheck the signature, not the form. Both a lambda and a named function can be stored when their signatures match.
Common mistake Confusing the return type with the argument type when reading the declaration
What it looks likeReading std::function<int(double)> as taking an int argumentWhy it failsIn function<
int(double)> the return type is int and the argument type is double.Do this insteadRead the first type as the return type and the type in parentheses as the argument type.
Practice Reading Signatures
A variable is declared as std::function<int(double)> fct. Which of the following callables can be assigned to it: a named function that returns int and takes a double, a lambda that returns int and takes a double, or a function that returns double and takes a double? Explain your answer using the signature rule.
Hints
- Compare each callable's return type and argument type with the pair declared inside the angle brackets.
- The form of the callable does not matter; only the signature does.
Key Takeaways
- std::function is a type that can hold any object you can invoke using the call operator, including ordinary functions, lambdas, and function objects.
- A std::function is declared with a specific return type and a specific argument type, such as function<
int(double)>, and this pair is the contract that every stored callable must satisfy. - A callable is accepted when its signature matches the declared signature; its written form, whether a named function, a lambda, or a function object, does not matter.
- An assignment fails when the argument type or return type differs from the declared one, as in the source's example where an int argument is assigned to a double-typed declaration.
- A function object is a class object whose class defines
operator(), and it is distinct from a plain named function and from an inline lambda.