Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why std::function has no function_type or equivalent member type?

From here it seems to me that the std::function has no function_type or equivalent member type to export the actual type used to initialize it.
It has result_type, argument_type, as well as first_argument_type and second_argument_type, but nothing like the type above mentioned.
Why it doesn't offer such a type as part of its interface?
There will be for sure a good reason for that, but I can't figure out what's that reason, so I'm just curious to find it out.


For I know the first question will be why do you need it, well, imagine that I want to do something like std::is_same<F1::function_type, F2::function_type>::value to check if their underlying types are the same in a sfinae evaluation, where it's fine if they contain different functions as long as the signs are the same.
I admit that it doesn't make much sense, to be honest the question is just for the sake of curiosity.

EDIT

As noted by @Brian in the comments of his answer, I misused the term initialize when I wrote:

to export the actual type used to initialize it

What I'm interested in is the template argument indeed.
As an example, for a std::function<void(S&, int)> (where S is a struct), function_type would be void(S&, int).

like image 435
skypjack Avatar asked Jan 06 '23 07:01

skypjack


2 Answers

I think you're asking the wrong question. The right question is: why should there be such a member type?

If, say, you write a function template that can accept any specialization of std::function, then the template parameter will already be immediately available to you:

template <typename T>
void f(std::function<T> F) {
    // you wouldn't write decltype(F)::function_type here; you'd just write T
}

The more inconvenient case is the one in which you have some function like

template <typename Callable>
void f(Callable C);

Here, you have no guarantee that Callable is a std::function specialization, so even if std::function<T> had typedef T function_type, you wouldn't want to access Callable::function_type for an arbitrary callable. So it wouldn't be of any use here.

The right question is: why do some standard library classes expose their template parameters, for example, containers of T (which have typedef T value_type)? The answer is that the standard has a specific set of requirements that the container types have to satisfy, which reflects a design goal that it should be possible to write generic algorithms that work on different types of containers, not all of which would be template specializations of the form C<T>. It then makes sense to mandate that all containers expose value_type because that's the only uniform way of extracting the element type from arbitrary containers.

If std::function were also an instance of some Callable concept, it would make sense to have the requirement that there is a function_type typedef so that code accepting any Callable could access the function type. But that's not the case, and it's not useful to have it for only the single template std::function.

like image 122
Brian Bi Avatar answered Feb 14 '23 19:02

Brian Bi


You can easily write one:

template < typename T >
struct function_type;

template < typename Sig >
struct function_type<std::function<Sig>> { using type = Sig; };

On your terminology: instantiate is the word you're looking for. You are looking for the type that the template was instantiated with.

The only people who know why it isn't a member type are those who designed the feature and those who voted it in (maybe). It could simply be something nobody thought of. It does seem a bit obvious, but that's in hindsight from the perspective of wanting it.

like image 42
Edward Strange Avatar answered Feb 14 '23 17:02

Edward Strange