function
Class TemplateForm ever follows function.
— “The Tall Office Building
Artistically Considered,”
from Lippincott’s Magazine
LOUIS HENRI SULLIVAN
One of the drawbacks of template programming is the proliferation of template instantiations. If you write a function template that takes a callable type as one of its type arguments and makes a function call through an object of that type, the code that is generated for an instantiation with a pointer to function is distinct from the code that is generated for an instantiation with a class type T1
that has a function call operator, and that code is distinct from the code that is generated for an instantiation with a different class type T2
that also has a function call operator. Further, additional template code that uses this template will also splinter into multiple instantiations, as will template code that uses this additional template code, and so on.[1]
Old-fashioned C++ designers recognize this as the classic case for the use of polymorphism.[2] By factoring the code into a common part and several specialized parts, one for each distinct callable type, and defining a uniform interface to these specialized parts, supported by an abstract class, we can give up a bit of speed in exchange for reduced code size.[3] All these details can be hidden by wrapping them in a template whose type argument provides the argument types and return type for the resulting template instance’s function call operator. Once this is done, a template function that calls through that specialization needs to be instantiated only once, regardless of how many callable types that wrapper is used with, because there is only one template specialization for the wrapper.
The argument types and return type are described by a function type, which is a return type followed by a left parenthesis followed by an argument list, which can be empty, followed by a right parenthesis. For example, the function type of the standard function float cosf(float)
is float(float)
; the function type of the standard function int rand()
is int()
.
The class template function
creates polymorphic function objects. The argument to the template must be a function type that describes the argument types and return type of objects of the template type. These objects can hold any target object that can be called with those argument types and returns a type that can be converted to the object’s return type. For example, the type function<float(float)>
can hold the function pointer cosf
as its target object. The target object can be reassigned at runtime.
template <class Fty> // Fty of type Ret(T1, T2, …, TN)
class function {
: public unary_function<T1, Ret> // see Section 9.5
: public binary_function<T1, T2, Ret> // see Section 9.5
public :
typedef Ret result_type;
function();
function(null_ptr_type npc);
function(const function & right);
template<class Fty2>
function(Fty2 right);
template<class Fty2>
function(reference_wrapper<Fty2> fnref);
function& operator=(const function& right);
function& operator=(null_ptr_type npc);
template<class Fty2>
function& operator=(Fty2 right);
template<class Fty2>
function& operator=(reference_wrapper<Fty2> fnref);
void swap(function& right);
operator boolean-type() const;
result_type operator()(T1, T2, …, TN) const;
const type_info & target_type() const;
template<class Fty2> Fty2 *target();
template<class Fty2> const Fty2 *target() const;
private :
template<class Fty2>
bool operator==(const Fty2&) const;
template<class Fty2>
bool operator!=(const Fty2&) const;
};
class bad_function_call : public std::exception {
};
An empty function object does not hold a callable object or a reference to a callable object. The class bad_function_call
describes an exception thrown to indicate that a call to operator()
on a function
object failed because the object was empty.
Some member functions take an operand that names a callable object. You can specify such an operand in several ways:
• fn:
A callable object. After the call, the function
object holds a copy of fn
.
• fnref:
A reference_wrapper
object holding a reference to a callable object. After the call, the function
object holds a reference to fnref.get()
.
• right:
Another function
object. After the call, the function
object holds the same callable object as right
.
• npc:
A null pointer constant. After the call, the function
object is empty. The member function takes an implementation-specific argument type that ensures that it cannot be called with a pointer that is not null or with an integral constant.
The member operators operator==
and operator!=
are private and not defined, so they cannot be called. But see Section 9.4 for comparisons with null pointers.
function
Objectfunction::function();
function::function(null_ptr_type npc);
function::function(const function& right);
template <class Fty2>
function::function(Fty2 fn);
template <class Fty2>
function::function(reference_wrapper <Fty2> fnref);
The first two constructors construct an empty function
object. The other constructors construct a function
object that holds the callable object passed as the operand.
Example 9.1. Constructing function
Objects (funobjfun/construct.cpp
)
#include <functional>
#include <iostream>
using std::tr1::function;
using std::cout;
int func()
{ // simple function
return 0;
}
struct S
{ // simple function object
int operator()() const
{
return 1;
}
typedef int result_type;
} obj;
void report (const char *title, bool val)
{ // report state of function object
cout << title << ": object is";
if (val)
cout << "not";
cout << "empty
";
}
int main()
{ // demonstrate construction of function<> types
function <int()> f0;
report("after default construction", f0);
function <int()> f1(0);
report("after construction from 0", f1);
function <int()> f2(f1);
report("after construction from f1", f2);
function <int()> f3(func);
report("after construction from func", f3);
function <int()> f4(obj);
report("after construction from obj", f4);
return 0;
}
function::operator boolean-type() const;
The member operator returns an object of an unspecified type that is convertible to bool
; the converted value is false
only if *this
is empty.
Example 9.2. Member operator boolean-type
(funobjfun/boolean.cpp
)
#include <functional>
#include <iostream>
#include <math.h>
using std::tr1::function;
using std::cout;
void report (const char *title, bool val)
{ // report state of function object
cout << title << ": object is ";
if (val)
cout << "not ";
cout << "empty
";
}
int main ()
{ // demonstrate conversion to boolean type
function <float(float)> fn; // construct empty object
report("after construction", fn);
fn = cosf; // assign target object
report("after assigning target object", fn);
fn = 0; // assign null pointer
report("after assigning null pointer", fn);
return 0;
}
function& function::operator=(const function& right);
function& function::operator=(null_ptr_type npc);
template<class Fty2>
function& function::operator=(Fty2 right);
template<class Fty2>
function& function::operator=(
reference_wrapper <Fty2> fnref);
The operators each replace the callable object, if any, held by *this
with the callable object passed as the operand.
Example 9.3. Assigning to function
Objects (funobjfun/assign.cpp
)
#include <functional>
#include <iostream>
using std::tr1::function;
using std::cout;
int func()
{ // simple function
return 0;
}
struct S
{ // simple function object
int operator()() const
{
return 1;
}
typedef int result_type;
} obj;
void report(const char *title, bool val)
{ // report state of function object
cout << title << ": object is ";
if (val)
cout << "not ";
cout << "empty
";
}
int main()
{ // demonstrate assignment of function<> types
function<int()> f0(func), f1;
report("constructed from func", f0);
f0 = f1;
report("assigned empty function object", f0);
f0 = func;
report("assigned func", f0);
f0 = 0;
report("assigned 0", f0);
f0 = obj;
report("assigned obj", f0);
return 0;
}
void function::swap(function& right);
The member function swaps, in constant time, the callable objects between *this
and right
and throws no exceptions.
Example 9.4. function::swap
(funobjfun/swap.cpp
)
#include <functional>
#include <iostream>
using std::tr1::function;
using std::cout;
int func()
{ // simple function
return 0;
}
void report(const char *title, bool val)
{ // report state of function object
cout << title << ": object is ";
if (val)
cout << "not ";
cout << "empty
";
}
int main()
{ // demonstrate swapping of function<> types
function<int()> f0;
function<int()> f1(func);
report("f0 before swap", f0);
report("f1 before swap", f1);
f0.swap(f1);
report("f0 after swap", f0);
report("f1 after swap", f1);
return 0;
}
template<class Fty>
bool operator==(const function<Fty>& func,
null_ptr_type npc);
template<class Fty>
bool operator==(null_ptr_type npc,
const function<Fty>& func);
The functions return true
only if the argument func
is empty. The argument npc
must be a null pointer constant.
Example 9.5. Null Pointer Constant (funobjfun/opequal.cpp
)
#include <functional>
#include <iostream>
#include <math.h>
using std::tr1::function;
using std::cout;
void report(const char *title, bool val)
{ // report state of function object
cout << title << ": object is ";
if (!val)
cout << "not ";
cout << "empty
";
}
int main()
{ // demonstrate comparison to null pointer constant
function<float(float)> fn; // construct empty object
report("after construction", fn == 0);
fn = cosf; // assign target object
report("after assigning target object", 0 == fn);
fn = 0; // assign null pointer
report("after assigning null pointer", fn == 0);
return 0;
}
template<class Fty>
bool operator!=(const function<Fty>&,
null_ptr_type npc);
template<class Fty>
bool operator!=(null_ptr_type npc,
const function<Fty>&);
The functions return true
only if the argument func
is not empty. The argument npc
must be a null pointer constant.
typedef Ret function::result_type;
The typedef is a synonym for the Ret
type in the template’s function type argument.
The template specialization function<Fn>
is derived from std::unary_-function<T1, Ret>
—hence defining the nested type result_type
as a synonym for Ret
and the nested type argument_type
as a synonym for T1
—only if Fn
is a function type that takes one argument.
The template specialization function<Fn>
is derived from std::binary_-function<T1, T2, Ret>
—hence defining the nested type result_type
as a synonym for Ret
, the nested type first_argument_type
as a synonym for T1
, and the nested type second_argument_type
as a synonym for T2
—only if Fn
is a function type that takes two arguments.
result_type function::operator()(
T1 t1, T2 t2, …, TN tN) const;
The member function throws an exception object of type bad_function_-call
if *this
is empty; otherwise, it returns INVOKE_R (fn, t1, t2, …, tN, Ret)
, where fn
is the callable object held by *this
.
Example 9.6. Invocation (funobjfun/invoke.cpp
)
#include <functional>
#include <iostream>
using std::tr1::function;
using std::cout;
int func()
{ // simple function
cout << "called func
";
return 0;
}
struct S
{ // simple function object
int operator()() const
{
cout << "called S::operator()
";
return 1;
}
typedef int result_type;
} obj;
int main()
{ // demonstrate construction of function<> types
function<int ()> f0(func);
f0();
f0 = obj;
f0();
return 0;
}
const type_info& function::target_type() const;
The member function returns a reference to an object of type type_info
that describes the type of the current target object.
Example 9.7. function::target_type
(funobjfun/targettype.cpp
)
#include <functional>
#include <iostream>
#include <typeinfo>
#include <math.h>
using std::tr1::function;
using std::cout; using std::type_info;
struct S
{ // simple callable type
float operator()(float) { return 1.0f; }
typedef float result_type;
};
void show_type(const char *title, const type_info& info)
{ // show name of target type
cout << title << ": " << info.name() << '
';
}
int main()
{ // demonstrate function::target_type
function<float(float)> fn;
show_type("empty", fn.target_type());
fn = cosf;
show_type("cosf", fn.target_type());
fn = S();
show_type("S", fn.target_type());
return 0;
}
template <class Fty2> Fty2 *function::target();
template <class Fty2> const Fty2 *function::target() const;
The member functions return a pointer to the target object if the target object is of type Fty2
; otherwise, they return a null pointer.
Each of these member functions is a function template. Since they do not take any arguments, the compiler can’t deduce the template argument types, so you must give the argument type explicitly when you call these functions.
Example 9.8. function::target
(funobjfun/target.cpp
)
#include <functional>
#include <iostream>
#include <typeinfo>
#include <math.h>
using std::tr1::function;
using std::cout; using std::type_info;
typedef function<float(float)> Fty;
typedef float(*fptr)(float);
struct S
{ // simple callable type
float operator()(float) { return 1.0f; }
typedef float result_type;
};
void show_pointer(const char *title, Fty& fty)
{ // check pointer type and value
cout << title << ": ";
if (fty.target<fptr>())
cout << " target has type pointer to function
";
else if (fty.target<S>())
cout << " target has type S
";
else
cout << " target is empty or has unknown type
";
}
int main()
{ // demonstrate function::target_type
function<float(float)> fn;
show_pointer("empty", fn);
fn = cosf;
show_pointer("cosf", fn);
fn = S();
show_pointer("S", fn);
return 0;
}
For each of the following errors, write a simple test case containing the error, and try to compile it. In the error messages, look for the key words that relate to the error in the code.
1. Attempting to construct a function
object with a callable type that takes the wrong number of arguments
2. Attempting to construct a function<float(float)>
object with a callable type that takes an argument of type void*
3. Attempting to compare two function
objects for equality
4. Attempting to compare a function
object to a pointer object
Given an object fn
of type function<double(double)>
, which of the following objects can be the target object of fn
? Write some code to verify your answers.
1. (double(*)(double))cos
,[4] declared in <math.h>
2. sinf
, declared in <math.h>
3. tanl
, declared in <math.h>
4. ilogbl
, declared in <math.h>
5. srand
, declared in <stdlib.h>
6. Fty f
, where Fty
is defined as:
struct Fty
{
float operator()(double);
typedef float result_type;
};
Write a class named match
that stores an integer value that is set to zero by the constructor. Add a member function that changes the stored value to the value of its argument. Add a function call operator that takes an integer argument and returns a Boolean value that is true if the value of the argument is equal to the object’s stored value.
Write a program that creates an object of type std::vector<int>
and stores several integer values in it.
1. Use the algorithm std::count_if
and a match
object to count the number of elements in the vector that are equal to 3.
2. Use the algorithm std::count_if
and a match
object to count the number of elements in the vector that are equal to 5.
3. Create a function
object that can hold an object of type match
as its target object. Initialize it with the match
object from the previous part, and pass the function
object to count_if
.
4. Change the stored value of the match
object to 7, store the modified match
object in the function
object, and repeat the search.
5. Store a reference to the match
object in the function
object, and repeat the search.
6. Change the stored value in the match
object to 9, and repeat the search.
3.144.222.132