As per the document, which says:
A Callable type is a type for which the INVOKE operation (used by,
e.g., std::function, std::bind, and std::thread::thread) is
applicable. This operation may be performed explicitly using the
library function std::invoke. (since C++17)
Requirements
The type T
satisfies Callable if Given f,
an object of type T ArgTypes,
suitable list of argument types R,
expressions must be valid:
std::declval()...) the expression is well-formed in
unevaluated contextExpression Requirements INVOKE(f,suitable return type The following
Question 1:
If I understand correctly, given void foo(int) {}, std::function(foo) is not callable, whereas std::function(foo, 1) is callable. Am I right?
Question 2:
What confuses me is the statement below.
As per the document, which says[emphasis mine]:
The class template std::packaged_task wraps any Callable target
(function, lambda expression, bind expression, or another function
object) so that it can be invoked asynchronously. Its return value or
exception thrown is stored in a shared state which can be accessed
through std::future objects.
As you see that the class template std::packaged_task wraps any Callable target, and std::function{foo} is not callable, but std::packged_task<void(int)> task{f}; compiles.
If one wants to be a successful professional, they do not stop reading the article on the introduction. If they continue reading the article and the referenced articles, they find all answers there.
Your first question is answered directly on that page:
otherwise, INVOKE(f, t1, t2, ..., tN) is equivalent to f(t1, t2, ..., tN) (that is, f is a FunctionObject).
Opening the reference:
A FunctionObject type is the type of an object that can be used on the left of the function call operator.
Therefore, void foo(int) {} is callable. The both std::function(foo) and std::function(foo, 1) will not compile. I guess you mean std::function f(foo), or std::function f{foo}, or std::function<void(int)> f(foo), the both are obviously callable, and I guess you confused std::bind(foo, 1) with std::function(foo, 1), that is also callable.
You know now, why std::packged_task<void(int)> task{f}; is compiled - because f is callable as it is figured out above.
Related
To make a concept checking if a type can be converted without narrowing to another, it is proposed here to make it using std::forward and std::type_identity_t like this:
template<class T, class U>
concept __construct_without_narrowing = requires (U&& x) {
{ std::type_identity_t<T[]>{std::forward<U>(x)} } -> T[1];
};
I understand from it why something like this:
To{std::declval<From>()}
gives incorrect results, but when i try to simplify it using another idea in the paper, writing just
template <typename From, typename To>
concept WithoutNarrowing =
requires (From x) {
{(To[1]){x}}
->std::same_as<To[1]>;
};
It seems to give the same results. What circumstances have to occur for it to give different result? Or is it equivalent? For what reason is std::forward used here?
This is the usual approach for type traits like this that involve some kind of function/constructor argument.
U is the type from which T is supposed to be constructed, but if we want to discuss the construction we also need to consider the value category of the argument. It may be an lvalue or a rvalue and this can affect e.g. which constructor is usable.
The idea is that we map the rvalue argument case to a non-reference U or rvalue reference U and the lvalue argument case to a lvalue reference U, matching the mapping of expressions in decltype and of return types with value categories in function call expressions.
Then, by the reference collapsing rules, U&& will be a lvalue reference if the constructor argument is a lvalue and otherwise a rvalue reference. Then using std::forward means that the actual argument we give to the construction will indeed be a lvalue argument when U was meant to represent one and a rvalue argument otherwise.
Your approach using {(To[1]){x}} doesn't use the forwarding and so would always only test whether construction from a lvalue can be done without narrowing, which is not what is expected if e.g. U is a non-reference.
Your approach is further incorrect because (To[1]){x} is not valid syntax in standard C++. If X is a type you can have X{x} or (X)x, but not (X){x}. The last syntax is part of C however and called a compound literal there. For that reason a C++ compiler may support it as an extension to C++. That's why the original implementation uses the round-about way with std::type_identity_t.
The implementation seems to also be written for an earlier draft of C++20 concepts. It is now not possible to use types to the right of -> directly for a requirement. Instead a concept, i.e. -> std::same_as<T[1]>, must be used as in your suggested implementation.
well there is difference between (U u), (U& u) and (U&& u) that std::forward is supposed to preserve. in case of (U u) the type has to have defined a copy constructor (since (U u) basically means "pass a copy of")
While trying to implement something similar to std::invoke, I noticed the following subtlety in the standard. I'm curious if anybody can give me a language lawyer explanation that helps decide whether libc++'s implementation is correct.
In N4868 (basically C++20), [func.invoke] defines std::invoke this way:
template<class F, class... Args>
constexpr invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
noexcept(is_nothrow_invocable_v<F, Args...>);
Returns: INVOKE(std::forward<F>(f), std::forward<Args>(args)...).
In turn, [func.require]/1 defines INVOKE as follows:
Define INVOKE(f, t_1, t_2, ..., t_N) as follows:
[...] when f is a pointer to a member function of a class T and [...]
[...] when f is a pointer to a member function of a class T and [...]
[...] when f is a pointer to a member function of a class T and [...]
[...] when N == 1 and f is a pointer to a data member of a class T and [...]
[...] when N == 1 and f is a pointer to a data member of a class T and [...]
f(t_1, t_2, ..., t_N) in all other cases.
My question is what should happen with the following snippet:
struct Callable {
int DoSomething() { return 17; }
};
int (Callable::*member_fn_ptr)() = &Callable::DoSomething;
assert(17 == std::invoke(member_fn_ptr, Callable{}));
This works under libc++, but in my implementation it causes a compilation error. That's because my implementation falls through to the "in all other cases" clause above, attempting to call the member function pointer as if it were a plain function pointer.
I thought this was a bug in my implementation until I read the standard a little more closely. The definition of std::invoke uses a forwarding reference for f, and clearly says it should be forwarded. In this case that means that it will be forwarded as an lvalue reference. Then in the cascade of logic in the definition of INVOKE, it seems like this should match the final case: f is not a pointer to a member function, but rather a reference to a pointer to a member function. And libc++'s implementation of <type_traits> agrees with me:
static_assert(!std::is_member_function_pointer_v<decltype(member_fn_ptr)&>)
In libc++ this works out only because the SFINAE for the first case of INVOKE checks whether std::decay_t<decltype(f)> is a member function pointer, not decltype(f).
Is this a bug in libc++, a bug in the standard, or a bug in my understanding of the standard?
In C++17 suppose I have a function-like object passed as a parameter to some template:
template<typename F>
void g(F f) {
auto x = f(/*...*/);
}
There are lots of different types F could be, such as a function pointer, a std::function, a lambda expression, and in fact any class type that implements operator().
Is there any way to get the function-like objects arity and type of its parameters and the type of its return type?
I mean, ultimately F could be a class that overloads operator() with multiple different member functions, each with different arities, parameter types and return types - so there isn't a fully-general answer (unless there is some way to iterate that overload set, which I don't think there is).
But for the typical case where a function call expression involving f results in a single overload, is there a solution?
(also if there is any progress in C++20, worth mentioning too)
C++17's adds deduction guides for std::function, which we can use to do the deduce the function signature of non-overloaded function-like objects:
template <typename R, typename... Args>
constexpr auto do_something_with_the_signature(std::function<R(Args...)>) {
// Assuming that you only care about the return type.
// Otherwise, you may want some kind of wrapper to extract the signature to
// avoid runtime cost
}
...
using some_type_computation =
decltype(do_something_with_the_signature(std::function(f)));
If you only wanted the return type, you could just use:
using result_type = typename decltype(std::function(f))::result_type;
If you want to avoid std::function altogether because of the compile-time costs, you can implement your own version of the deduction guides for your own type (possibly as general as a function_traits type trait). A sketch of how you might implement the deduction guides yourself can be seen in my answer here: https://stackoverflow.com/a/66038056/1896169
This question (Why does INVOKE facility in the C++11 standard refer to data members?) asks why INVOKE discusses data members but ignores how they are actually invoked.
This question (What is std::invoke in c++?) discusses why they are accessed, but why they are not called if callable.
[func.require]:
Define INVOKE(f, t1, t2, …, tN) as follows:
(1.1) (t1.*f)(t2, …, tN) when f is a pointer to a member function of a class T and is_base_of_v<T, remove_reference_t<decltype(t1)>> is true;
(1.2) (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of a class T and remove_cvref_t<decltype(t1)> is a specialization of reference_wrapper;
(1.3) ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a class T and t1 does not satisfy the previous two items;
(1.4) t1.*f when N == 1 and f is a pointer to data member of a class T and is_base_of_v<T, remove_reference_t<decltype(t1)>> is true;
(1.5) t1.get().*f when N == 1 and f is a pointer to data member of a class T and remove_cvref_t<decltype(t1)> is a specialization of reference_wrapper;
(1.6) (*t1).*f when N == 1 and f is a pointer to data member of a class T and t1 does not satisfy the previous two items;
(1.7) f(t1, t2, …, tN) in all other cases.
1.4 through 1.6 deal with access to pointers to data members, which makes sense given functors and stored callables. What I don't understand is why it doesn't call those members, but instead simply dereferences them? I would expect that 1.4 would parallel the syntax of 1.1 and er...invoke the object in question if f possesses an operator ().
Why is this restriction in place, and what purpose does it serve?
Here's some code for clarification:
#include <functional>
#include <iostream>
struct func1
{
void operator()() { std::cout << "Invoked functor\n"; }
};
void func2()
{
std::cout << "Invoked free function\n";
}
struct D1 {};
struct T1 {
func1 f1;
void func3() { std::cout << "Invoked member function\n"; }
D1 d1;
};
int main()
{
T1 t1;
func1 free_f1;
std::invoke(&T1::f1, t1); //does nothing
std::invoke(&func1::operator(), t1.f1); //okay, so there is a workaround, if clumsy
std::invoke(&func2); //calls func2
std::invoke(&T1::func3, t1); //calls func3
std::invoke(&T1::d1, t1); //does nothing (expected)
std::invoke(free_f1); //works on non-member functors
return 0;
}
This compiles nicely, but only calls func1() on the second call to invoke. I understand why INVOKE does nothing but dereference the first argument when it is not a callable object. My question is why does the standard not allow for calling callable pointers to data members, i.e. why does the Standard not mandate that f1 is called in the first usage of std::invoke above?
Edit: since std::invoke was added in C++17, I'm tagging this question as such hoping someone involved in the process can shed some light.
Here's the original paper for adding std::invoke(), which actually explains in its motivation that it wants to handle functors uniformly:
Although the behaviour of the INVOKE expression may be reproduced by combination of the existing standard library components, separate treatment of the functors and member pointers is required in such solutions.
In the code above, you can see this works...just not for pointers to member data that are functors themselves. Is this simply an oversight?
When C++11's standard library was being assembled, it took on a number of features from a variety of Boost libraries. For the purpose of this conversation, the following Boost tools matter:
bind
function
reference_wrapper
mem_fn
These all relate to, on one level or another, a callable thing which can take some number of arguments of some types and results in a particular return value. As such, they all try to treat callable things in a consistent way. C++11, when it adopted these tools, invented the concept of INVOKE in accord with perfect forwarding rules, so that all of these would be able to refer to a consistent way of handling things.
mem_fn's sole purpose is to take a pointer to a member and convert it into a thing directly callable with (). For pointers to member functions, the obvious thing to do is to call the member function being pointed at, given the object and any parameters to that function. For pointers to member variables, the most obvious thing to do is to return the value of that member variable, given the object to access.
The ability to turn a data member pointer into a unary functor that returns the variable itself is quite useful. You can use something like std::transform, passing it mem_fn of a data member pointer to generate a sequence of values that accesses a specific accessible data member. The range features added to C++20 will make this even more useful, as you can create transformed ranges, manipulating sequences of subobjects just by getting a member pointer.
But here's the thing: you want this to work regardless of the type of that member subobject. If that subobject just so happens to be invokable, mem_fn ought to be able to access the invokable object as though it were any other object. The fact that it happens to be invokable is irrelevant to mem_fn's purpose.
But boost::function and boost::bind can take member pointers. They all based their member pointer behavior on that of boost::mem_fn. Therefore, if mem_fn treats a pointer to a data member as a unary functor returning the value of that member, then all of these must treat a pointer to a data member that way.
So when C++11 codified all of these into one unifying concept, it codified that practice directly into INVOKE.
So from an etymological perspective, that is why INVOKE works this way: because all of these are meant to treat callables identically, and the whole point of mem_fn with regard to data member pointers is to treat them as unary functions that return their values. So that's how everyone else has to treat them too.
And isn't that a good thing? Is that not the correct behavior? Would you really want to have pointers to data members behave wildly differently if the type the member pointer points to happens to be callable than if it doesn't? That would make it impossible to write generic code that takes data member pointers, then operates on some sequence of objects. How would you be able to generically access a data member pointer, if you weren't sure it would get the subobject being reference or invoke the subobject itself?
With c++17 we have fancy new is_invocable and fancy new prvalues that aren't really values.
This permits you to create an object without having to first logically construct it, then elide the construction.
I have run into a problem where using std::is_invocable to test if you can call something, and prvalue rules, seem to collide:
struct no_move {
no_move(no_move&&)=delete;
explicit no_move(int) {}
};
void f( no_move ) {}
now can we ask if f can be invoked using a prvalue of type no_move?
f( no_move(1) )
std::is_invocable< decltype(&f), no_move > doesn't work because it uses std::declval<no_move>() which is an xvalue like no_move&& not a prvalue of type no_move.
In c++14 this was the same, but guaranteed elision makes some functions callable with an xvalue (i.e., "T&&") and others with prvalues of type T.
Is there an alternative, or do we have to invent our own trait to handle this case?
(In a theoretical world where std::declval<T> returned T instead of T&&, is_invocable would, I believe, do the right thing).
You are misusing the Invocable concept. This concept means nothing more than the ability to use std::invoke on the given function and the provided arguments.
You can't do std::invoke(f, no_move(1)), as this would provoke a copy/move of the forwarded argument. It is impossible for a prvalue to be used as a parameter through a forwarded call like invoke. You can pass a prvalue to the forwarding call, but the eventual call to the given function will get an xvalue.
This is a good reason to avoid using immobile types as value parameters in functions. Take them by const& instead.
C++ does not have a type trait to see if a function can be called with specific parameters in the way that you want.
Is there an alternative, or do we have to invent our own trait to handle this case?
Yeah, you'd just have to write your own trait that doesn't use declval. Assuming you have std::is_detected lying around (which I know you certainly do):
template <typename T> T make();
template <typename F, typename... Args>
using invoke_result_t = decltype(std::declval<F>()(make<Args>()...));
// ^^^^^^^^^^^^^ ^^^^^
template <typename F, typename... Args>
using is_invocable = std::is_detected<invoke_result_t, F, Args...>;
This way, std::is_invocable<decltype(f), no_move> is false_type, but is_invocable<decltype(f), no_move)> is true_type.
I intentionally use declval<F>() for the function instead of make so as to allow using decltype(f) here. Really, invoke_result_t should be more complicated, and "do the right thing" for pointers to members, etc. But this is at least a simple approximation that indicates the viability of this approach.