This library consistently uses the term 'function' where P0172 uses 'member':
function
: a typeF
for whichstd::is_function<F>
istrue_type
.
Not to be confused withstd::function
: a wrapper forCallable
types.
This unfortunate C++11 naming clash muddied future std
usage of 'function'.
Despite this, the term 'function' has established meaning, in the traits domain,
as 'C++ function type' in general, including abominable function types.
On the other hand, outside of the traits domain, 'C++ function' or 'function' itself
implies a free function whose type is never abominable. Now, std::function
has conflated 'function' with function objects and callables of all kinds.
With function_traits
squarely in the traits domain and wholly outwith the
std
namespace, it is entirely appropriate to reappropriate the term 'function'.
function_traits
uses ltl::function_
prefix for all but one of its traits.
The one remaining trait uses _function
suffix to refine std::is_function
;
ltl::is_free_function
is true for non-abominable functions.
P0172 uses '_member
', e.g. is_const_member
, add_member_const
This usage is not justified in the paper, though it is discussed at the end of
section 2.1 "Definition" (my emphasis):
Note that it is not possible to create a function that has an abominable type.
Rather, the cv-ref qualifier applies to the implicit*this
reference when
calling a member function. However, abominable function types are
specifically function types, and not member function types.
This is evident from the lack of ability to specify the type of class the
abominable function would be a member of, when declaring such a type.
- P0172R0 Abominable Function Types by Alisdair Meredith, Nov 2015
The emphasized text appears to argue against the use of 'member' to refer
to general function types, whether abominable or not, and argue for 'function'.
To be more precise, there is no such thing as a member function type, only the
related type of its pointer-to-member-function so, equally, free function types
are not (pointer-to-) member function types. It is odd to use the term 'member'
for general function types that include the types of free functions.
On the other hand, it is true that general function types, abominable or not,
can be extracted from (pointer-to-) member function types whereas the types
of free functions can only be non-abominable. In this sense, 'member' may be
used to emphasize 'general' function type.
Within the type system, function types merely model aspects of actual functions
as needed for type checking. They need not be bound to function entities at all.
Types can be used to encode pure information. Function types are rich - their
parameters encode a tuple of (decayed) types and their qualifiers can encode
24 or 48 (with varargs) discrete values.
Section 2.5 explains "Why do Abominable Function Types Exist".
Adapting the example code:
struct S { void f() const; };
template <typename F> auto type(F S::*) -> F;
using F = decltype(type(&S::f));
Given a const-qualified member function f
of S
:
&S::f
is a pointer-to-member-function of type void(S::*)() const
or, more concisely, F S::*
with F
= void() const
So, both 'member' and 'function' are misleading terms on their own.
This library, outside the std
namespace, is free to use 'function'
as the more consistent choice.
This library always puts function
up front and qualifiers at the end.
E.g. for the predicates:
P0172 proposed std name |
function_trait name |
---|---|
is_const_member |
function_is_const |
is_volatile_member |
function_is_volatile |
is_cv_member |
function_is_cv |
is_lvalue_reference_member |
function_is_reference_lvalue |
is_rvalue_reference_member |
function_is_reference_rvalue |
has_member_qualifiers |
function_is_cvref |
is_noexcept (Boost.CallableTraits) |
function_is_noexcept |
A function_trait name such as function_is_const
can be read more verbosely as function_type_is_const_qualified
(the is_noexcept trait reads as function_type_has_noexcept_specifier
)
Why put 'function
' upfront?
Why not put 'is
' upfront, as is convention for predicates?
is_function_const
| is_function_type_const_qualified
?
-
In this design, the global trait name reflects the member trait name
function_is_noexcept<F>
'global trait'
function<F>::is_noexcept
'member trait' -
The
function_traits
are only defined forfunction
types.
For use with non-function types, guard withstd::is_function
:
std::is_function_v<T> && function_is_noexcept_v<T>
- This reads "is the type T a function and, if so, is it noexcept?".
This combined trait is more appropriately calledis_function_noexcept
.
From P0172:
Some obvious issues with this proposal are deferred [...]. For example,
it is not clear whether the following should be supported, or what it should mean:
using result = add_member_lvalue_reference_t<void() &&>;
More examples: the 'mutating' traits:
P0172 proposed std name |
function_trait name |
---|---|
add_member_const |
function_add_const |
add_member_volatile |
function_add_volatile |
add_member_cv |
function_add_cv |
add_member_lvalue_reference |
function_set_reference_lvalue |
add_member_rvalue_reference |
function_set_reference_rvalue |
remove_member_const |
function_remove_const |
remove_member_volatile |
function_remove_volatile |
remove_member_cv |
function_remove_cv |
remove_member_lvalue_reference |
function_remove_reference |
remove_member_rvalue_reference |
function_remove_reference |
remove_all_member_qualifiers |
function_remove_cvref |
Note how 'set
' is used for function reference qualifier in place of add
.
Compound types and compounding
Arbitrarily complex types are constructed from fundamental types by compounding;
adding pointers, array extents or top-level reference, or forming enums or classes
or function types:
A C++ function type is a compound type:
Its function 'signature' is a compound of its return type and argument types.
Anycvref
qualifiers ornoexcept
are not compounded onto the signature -
they are part of the function type.
Forming a function type is a compounding operation.
Changing the type itself is not a compounding operation.
Clearly, changing its signature changes it to a different function type.
Similarly, changing cvref qualifiers changes the type itself to a differently-
qualified type, even though with the same signature. In other words,
the function qualifiers do not 'compound' or 'nest' onto a base signature.
The add
and remove
variants of conventional std
traits have 'compounding'
as a mental model; add_pointer
and add_extent
compound their base type.
On the other hand, adding cv
or ref
qualifiers does not always compound -
cv
qualifiers attach to their base type, so a cv-qualified type is not a compound,
while a reference qualifier can only be
and 'decompounding' modifiers. For instance, adding a
reference qualifier
function_trait<F> | P0172R0 / callable_trait<F> |
---|---|
is_function_*<F> |
is_*_member<F> |
Predicate traits
Predicate traits test a true-or-false property of a type
returning a type derived from std::bool_constant
i.e. inherited from std::true_type
or std::false_type
The bool value itself can be extracted from the bool_constant
via its value
member or by invoking its function call operator.
Alternatively, a _v
suffix defines a templated boolean variable -
i.e. is_*_v
directly gives true
or false
value for property *
.
For example, these are all equivalent:
function_is_noexcept<F>::value
function_is_noexcept<F>()
function_is_noexcept_v<F>
function_trait<F> | P0172R0 / callable_trait<F> |
---|---|
is_free_function<F> |
not has_member_qualifiers<F> |
function_trait<F> | P0172R0 / callable_trait<F> |
---|---|
is_free_function<F> |
not has_member_qualifiers<F> |
function_is_const<F> |
is_const_member<F> |
function_is_volatile<F> |
is_volatile_member<F> |
function_is_cv<F> (Note: const OR volatile) |
is_cv_member<F> (Note: const AND volatile) |
function_is_reference_lvalue<F> |
is_lvalue_reference_member<F> |
function_is_reference_rvalue<F> |
is_rvalue_reference_member<F> |
function_is_reference<F> |
is_reference_member<F> |
function_is_cvref<F> |
has_member_qualifiers<F> |
function_is_noexcept<F> |
is_noexcept<F> |
function_is_variadic<F> |
has_varargs<F> |
Modifying traits: add | remove
function_trait<F> | P0172R0 / callable_trait<F> |
---|---|
function_add_const<F> function_set_const<F,true> function_set_cv<F,true,false> |
add_member_const<F> |
function_remove_const<F> |
remove_member_const<F> |
function_add_volatile<F> function_set_volatile<F,true> function_set_cv<F,false,true> |
add_member_volatile<F> |
function_remove_volatile<F> |
remove_member_volatile<F> |
function_set_cv<F,true,true> |
add_member_cv<F> |
function_remove_cv<F> |
remove_member_cv<F> |
function_set_reference_lvalue<F> function_set_reference_lvalue_ |
add_member_lvalue_reference<F> |
function_set_reference_rvalue<F> function_set_reference_rvalue_ |
add_member_rvalue_reference<F> |
function_remove_reference<F> |
remove_member_reference<F> |
function_remove_cvref<F> |
c.f. function_type |
function_signature<F> function_signature_noexcept<F> function_remove_cvref<F> |
function_type<F> |
function_set_cvref<F,bool,bool,ref> function_set_cvref_as<F,G> |
|
function_add_noexcept<F> function_set_noexcept<F,true> |
add_noexcept<F> |
function_remove_noexcept<F> function_set_noexcept<F,false> |
remove_noexcept<F> |
function_add_variadic<F> function_set_variadic<F,true> |
add_varargs<F> |
function_remove_variadic<F> |
remove_varargs<F> |
function_set_return_type<F,R> |
apply_return<F,R> |
function_set_signature<F,S> |
|
function_arg_types<F,tuple_type> |
args<F,tuple_type> |
function_return_type<F> |
return_type<F> |
function_signature<F> |
c.f. function_type |
qualified_class_of<F> |
|
remove_transaction_safe<F> |
|
add_transaction_safe<F> |
|
class_of<F> |
|
apply_member_pointer<F> |