pineapple-src/externals/vcpkg/buildtrees/boost-utility/src/ost-1.79.0-8813e85352.clean/doc/call_traits.qbk
2022-11-05 15:35:56 +01:00

443 lines
16 KiB
Text
Executable file

[/
/ Copyright (c) 2012 Marshall Clow
/ Copyright (c) 2021, Alan Freitas
/
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/]
[/===============]
[section Call Traits]
[/===============]
[section Introduction]
All of the contents of [@../../../../boost/call_traits.hpp `<boost/call_traits.hpp>`] are
defined inside `namespace boost`.
The template class __call_traits_T__ encapsulates the
"best" method to pass a parameter of some type `T` to or
from a function, and consists of a collection of `typedef`s defined
as in the table below. The purpose of __call_traits__ is to ensure
that problems like [link sec:refs "references to references"]
never occur, and that parameters are passed in the most efficient
manner possible, as in the [link sec:examples examples]. In each
case, if your existing practice is to use the type defined on the
left, then replace it with the __call_traits__ defined type on the
right.
Note that for compilers that do not support either partial
specialization or member templates, no benefit will occur from
using __call_traits__: the __call_traits__ defined types will always be
the same as the existing practice in this case. In addition if
only member templates and not partial template specialisation is
support by the compiler (for example Visual C++ 6) then
__call_traits__ cannot be used with array types, although it can still be
used to solve the reference to reference problem.
[table __call_traits__ types
[[Existing practice] [__call_traits__ equivalent] [Description] [Notes]]
[
[`T`
(return by value)
]
[
__call_traits_T__`::value_type`
]
[
Defines a type that represents the "value" of type `T`.
Use this for functions that return by value, or possibly for stored values of type `T`.
]
[2]
]
[
[`T&`
(return value)
]
[
__call_traits_T__`::reference`
]
[
Defines a type that represents a reference to type `T`.
Use for functions that would normally return a `T&`.
]
[1]
]
[
[`const T&`
(return value)
]
[
__call_traits_T__`::const_reference`
]
[
Defines a type that represents a constant reference to type `T`.
Use for functions that would normally return a `const T&`.
]
[1]
]
[
[`const T&`
(function parameter)
]
[
__call_traits_T__`::param_type`
]
[
Defines a type that represents the "best" way to pass a parameter of type `T` to a function.
]
[1,3]
]
]
Notes:
# If `T` is already reference type, then __call_traits__ is
defined such that [link sec:refs "references to references"]
do not occur (requires partial specialization).
# If `T` is an array type, then __call_traits__ defines `value_type`
as a "constant pointer to type" rather than an
"array of type" (requires partial specialization).
Note that if you are using `value_type` as a stored value
then this will result in storing a "constant pointer to
an array" rather than the array itself. This may or may
not be a good thing depending upon what you actually
need (in other words take care!).
# If `T` is a small built in type or a pointer, then `param_type`
is defined as `T const`, instead of `T const&`. This can
improve the ability of the compiler to optimize loops in
the body of the function if they depend upon the passed
parameter, the semantics of the passed parameter is
otherwise unchanged (requires partial specialization).
[endsect]
[section Copy constructibility]
The following table defines which __call_traits__ types can always
be copy-constructed from which other types:
[table Which __call_traits__ types can always be copy-constructed from which other types
[[] [To `T`] [To `value_type`] [To `reference`] [To `const_reference`] [To `param_type`]]
[[From `T`] [iff `T` is copy constructible] [iff `T` is copy constructible] [Yes] [Yes] [Yes]]
[[From `value_type`] [iff `T` is copy constructible] [iff `T` is copy constructible] [No] [No] [Yes]]
[[From `reference`] [iff `T` is copy constructible] [iff `T` is copy constructible] [Yes] [Yes] [Yes]]
[[From `const_reference`] [iff `T` is copy constructible] [No] [No] [Yes] [Yes]]
[[From `param_type`] [iff `T` is copy constructible] [iff `T` is copy constructible] [No] [No] [Yes]]
]
If `T` is an assignable type the following assignments are possible:
[table Which __call_traits__ types are assignable from which other types
[[] [To `T`] [To `value_type`] [To `reference`] [To `const_reference`] [To `param_type`]]
[[From `T`] [Yes] [Yes] [-] [-] [-]]
[[From `value_type`] [Yes] [Yes] [-] [-] [-]]
[[From `reference`] [Yes] [Yes] [-] [-] [-]]
[[From `const_reference`] [Yes] [Yes] [-] [-] [-]]
[[From `param_type`] [Yes] [Yes] [-] [-] [-]]
]
[endsect]
[#sec:examples]
[section Examples]
The following table shows the effect that __call_traits__ has on
various types.
[table Examples of __call_traits__ types
[[] [__call_traits__::`value_type`] [__call_traits__::`reference`] [__call_traits__::`const_reference`] [__call_traits__::`param_type`] [Applies to:]]
[[From `my_class`] [`my_class`] [`my_class&`] [`const my_class&`] [`my_class const&`] [All user-defined types]]
[[From `int`] [`int`] [`int&`] [`const int&`] [`int const`] [All small built-in types]]
[[From `int*`] [`int*`] [`int*&`] [`int* const &`] [`int* const`] [All pointer types]]
[[From `int&`] [`int&`] [`int&`] [`const int&`] [`int&`] [All reference types]]
[[From `const int&`] [`const int&`] [`const int&`] [`const int&`] [`const int&`] [All constant reference types]]
[[From `int[3]`] [`const int*`] [`int(&)[3]`] [`const int(&)[3]`] [`const int* const`] [All array types]]
[[From `const int[3]`] [`const int*`] [`const int(&)[3]`] [`const int(&)[3]`] [`const int* const`] [All constant array types]]
]
The table assumes the compiler supports partial
specialization: if it does not then all types behave in
the same way as the entry for "`my_class`", and
__call_traits__ can not be used with reference or array types.
[section Example 1:]
The following class is a trivial class that stores some type `T`
by value (see the [@../../../test/call_traits_test.cpp `call_traits_test.cpp`]
file). The aim is to illustrate how each of the available
__call_traits__ `typedef`s may be used:
```
template <class T>
struct contained
{
// define our typedefs first, arrays are stored by value
// so value_type is not the same as result_type:
typedef typename __boost_call_traits__<T>::param_type param_type;
typedef typename __boost_call_traits__<T>::reference reference;
typedef typename __boost_call_traits__<T>::const_reference const_reference;
typedef T value_type;
typedef typename __boost_call_traits__<T>::value_type result_type;
// stored value:
value_type v_;
// constructors:
contained() {}
contained(param_type p) : v_(p){}
// return byval:
result_type value() { return v_; }
// return by_ref:
reference get() { return v_; }
const_reference const_get()const { return v_; }
// pass value:
void call(param_type p){}
};
```
[endsect]
[#sec:refs]
[section Example 2 (the reference to reference problem):]
Consider the definition of __std_binder1st__:
```
template <class Operation>
class binder1st :
public __std_unary_function__<typename Operation::second_argument_type, typename Operation::result_type>
{
protected:
Operation op;
typename Operation::first_argument_type value;
public:
binder1st(const Operation& x, const typename Operation::first_argument_type& y);
typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const;
};
```
Now consider what happens in the relatively common case that
the functor takes its second argument as a reference, that
implies that `Operation::second_argument_type` is a
reference type, `operator()` will now end up taking a
reference to a reference as an argument, and that is not
currently legal. The solution here is to modify `operator()`
to use __call_traits__:
```
typename Operation::result_type operator()(typename __call_traits__<typename Operation::second_argument_type>::param_type x) const;
```
Now in the case that `Operation::second_argument_type`
is a reference type, the argument is passed as a reference, and
the no "reference to reference" occurs.
[endsect]
[#sec:example3]
[section Example 3 (the `make_pair` problem):]
If we pass the name of an array as one (or both) arguments to `__std_make_pair__`,
then template argument deduction deduces the passed parameter as
"const reference to array of `T`", this also applies to
string literals (which are really array literals). Consequently
instead of returning a pair of pointers, it tries to return a
pair of arrays, and since an array type is not copy-constructible
the code fails to compile. One solution is to explicitly cast the
arguments to __std_make_pair__ to pointers, but __call_traits__ provides a
better automatic solution that works safely even in generic code where the
cast might do the wrong thing:
```
template <class T1, class T2>
__std_pair__<
typename __boost_call_traits__<T1>::value_type,
typename __boost_call_traits__<T2>::value_type>
make_pair(const T1& t1, const T2& t2)
{
return __std_pair__<
typename __boost_call_traits__<T1>::value_type,
typename __boost_call_traits__<T2>::value_type>(t1, t2);
}
```
Here, the deduced argument types will be automatically
degraded to pointers if the deduced types are arrays, similar
situations occur in the standard binders and adapters: in
principle in any function that "wraps" a temporary
whose type is deduced. Note that the function arguments to
__std_make_pair__ are not expressed in terms of __call_traits__: doing so
would prevent template argument deduction from functioning.
[endsect]
[#sec:example4]
[section Example 4 (optimising fill):]
The __call_traits__ template will "optimize" the passing
of a small built-in type as a function parameter. This mainly has
an effect when the parameter is used within a loop body.
In the following example (see [@boost:/libs/type_traits/examples/fill_example.cpp `fill_example.cpp`]),
a version of __std_fill__ is optimized in two ways: if the type
passed is a single byte built-in type then __std_memset__ is used to
effect the fill, otherwise a conventional C++ implementation is
used, but with the passed parameter "optimized" using
__call_traits__:
```
template <bool opt>
struct filler
{
template <typename I, typename T>
static void do_fill(I first, I last, typename __boost_call_traits__<T>::param_type val)
{
while(first != last)
{
*first = val;
++first;
}
}
};
template <>
struct filler<true>
{
template <typename I, typename T>
static void do_fill(I first, I last, T val)
{
__std_memset__(first, val, last-first);
}
};
template <class I, class T>
inline void fill(I first, I last, const T& val)
{
enum { can_opt = boost::is_pointer<I>::value
&& boost::is_arithmetic<T>::value
&& (sizeof(T) == 1) };
typedef filler<can_opt> filler_t;
filler_t::template do_fill<I,T>(first, last, val);
}
```
The reason that this is "optimal" for small built-in types is that
with the value passed as `T const` instead of `const T&` the compiler is
able to tell both that the value is constant and that it is free
of aliases. With this information the compiler is able to cache
the passed value in a register, unroll the loop, or use
explicitly parallel instructions: if any of these are supported.
Exactly how much mileage you will get from this depends upon your
compiler - we could really use some accurate benchmarking
software as part of boost for cases like this.
Note that the function arguments to fill are not expressed in
terms of __call_traits__: doing so would prevent template argument
deduction from functioning. Instead fill acts as a "thin
wrapper" that is there to perform template argument
deduction, the compiler will optimise away the call to fill all
together, replacing it with the call to `filler<>::do_fill`,
which does use __call_traits__.
[endsect]
[endsect]
[section Rationale]
The following notes are intended to briefly describe the
rationale behind choices made in __call_traits__.
All user-defined types follow "existing practice" and need no comment.
Small built-in types, what the standard calls [@https://en.cppreference.com/w/cpp/language/types fundamental
types], differ from existing practice only in the `param_type`
`typedef`. In this case passing `T const` is compatible
with existing practice, but may improve performance in some cases
(see [link sec:example4 Example 4]). In any case this should never
be any worse than existing practice.
Pointers follow the same rationale as small built-in types.
For reference types the rationale follows [link sec:refs Example 2]
- references to references are not allowed, so the __call_traits__
members must be defined such that these problems do
not occur. There is a proposal to modify the language such that
"a reference to a reference is a reference" (issue #106,
submitted by Bjarne Stroustrup). __call_traits_T__`::value_type`
and __call_traits_T__`::param_type` both provide the same effect
as that proposal, without the need for a language change. In
other words, it's a workaround.
For array types, a function that takes an array as an argument
will degrade the array type to a pointer type: this means that
the type of the actual parameter is different from its declared
type, something that can cause endless problems in template code
that relies on the declared type of a parameter.
For example:
```
template <class T>
struct A
{
void foo(T t);
};
```
In this case if we instantiate `A<int[2]>` then the declared type of
the parameter passed to member function `foo` is `int[2]`, but its
actual type is `const int*`. If we try to use the type `T` within the
function body, then there is a strong likelihood that our code will not compile:
```
template <class T>
void A<T>::foo(T t)
{
T dup(t); // doesn't compile for case that T is an array.
}
```
By using __call_traits__ the degradation from array to pointer is
explicit, and the type of the parameter is the same as it's
declared type:
```
template <class T>
struct A
{
void foo(typename __call_traits__<T>::value_type t);
};
template <class T>
void A<T>::foo(typename __call_traits__<T>::value_type t)
{
typename __call_traits__<T>::value_type dup(t); // OK even if T is an array type.
}
```
For `value_type` (return by value), again only a pointer may be
returned, not a copy of the whole array, and again __call_traits__
makes the degradation explicit. The `value_type` member is useful
whenever an array must be explicitly degraded to a pointer -
[link sec:example3 Example 3] provides the test case.
Footnote: the array specialisation for __call_traits__ is the least
well understood of all the __call_traits__ specialisations. If the given
semantics cause specific problems for you, or does not solve a particular
array-related problem, then I would be interested to hear about
it. Most people though will probably never need to use this
specialisation.
[endsect]
[/===============]
[xinclude tmp/call_traits_reference.xml]
[/===============]
[endsect]