I have come across a use case where std::mem_fn cannot do something that a hand-rolled wrapper function can. It comes up when the wrapper function is used on something that's not of the method's class, but a type implicitly convertible to it:
#include <functional>
struct A
{
};
struct B
{
B(A); // implicit conversion from A to B
void foo() const;
};
auto foo1 = std::mem_fn(&B::foo); // std::mem_fn
void foo2(const B& b) { b.foo(); } // hand-rolled wrapper
int main()
{
A a;
foo1(a); // doesn't work
foo2(a); // works fine
}
The compiler error for the call to foo1 is the following (with GCC 4.8):
In file included from test.cpp:1:0:
functional: In instantiation of '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::_M_call(_Tp&, const volatile void*, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]':
functional:608:42: required from '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::operator()(_Tp&, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]'
test.cpp:21:11: required from here
functional:586:13: error: no match for 'operator*' (operand type is 'A')
{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
^
Would it have been possible to implement std::mem_fn in such a way that this use case works just like it does with the hand-rolled wrapper?
It would be possible, yes, but it's not how the C++ standard specifies mem_fn.
The standard says that foo1(a) calls INVOKE(&B::foo, a) where that is defined in [func.require] as:
Define INVOKE (f, t1, t2, ..., tN) as follows:
— (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
— ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
— ...
Your case fails to meet the conditions of the first bullet, because a is not an object of class B, nor a reference to a B or a class derived from B, so the second bullet applies and so it's equivalent to ((*a).*f)() which isn't valid.
It's defined this way to allow smart pointers to be used, e.g.
auto foo1 = std::mem_fn(B::foo);
auto p = std::make_shared<B>();
foo1(p);
The definition of INVOKE (which is also used by bind, function, async and other parts of the library that create call wrappers) means that when invoking a wrapped pointer-to-member, if the first argument t1 isn't a T then it is assumed to be some kind of pointer and gets dereferenced. This means it works with std::shared_ptr and std::unique_ptr but also with types that std::mem_fn knows nothing about, such as boost::shared_ptr and MyVeryOwnSmartPtr.
To make your code work it would be possible to add extra cases to handle when t1 is not a T or a type derived from T, but is_convertible<T>::value is true, and to invoke T(t1).*f)(), but that would complicate the specification and might have undesirable consequences in some cases.
Your "wrapper" will force the implicit conversion of its argument, but it can't handle smart pointers or rvalues of type B, which are both supported by mem_fn. If you have a specific case where you want to convert A objects to B to call the function, then just do that, the generic mem_fn template isn't suitable, but it is more flexible and generic and works in plenty of other situations.
(N.B. the definition of INVOKE is actually defective because it dereferences std::reference_wrapper objects in the same way as it dereferences your a argument. I've proposed a fix at http://cplusplus.github.com/LWG/lwg-active.html#2219, but that doesn't affect your example.)
Related
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
int main ()
{
function<void(const Foo*, int)> func = &Foo::print_add;
function<void(const Foo&, int)> func2 = &Foo::print_add;
Foo f(1), f2(2);
func(&f, 2);
func2(f2, 3);
return 0;
}
why func and func2 both can call member function Foo::print_add correctly? they have different callable type as template argument. From what I know, ‘this’ pointer is passed as a hidden argument to all nonstatic member function calls, the callable type of func2 recieve const Foo& as argument which doesn't match the type of this pointer. why the code above doesn't generate a compiler error?
std::function stores and can "invoke any CopyConstructible Callable target".
The Callable named requirement is defined as follows:
if f is a pointer to member function of class T:
If std::is_base_of<T, std::remove_reference_t<decltype(t1)>>::value
is true, then INVOKE(f, t1, t2, ..., tN) is equivalent to
(t1.*f)(t2, ..., tN)
Or, in other words: if after removing any reference qualifier on the first parameter the end result is the member function's class (or a subclass), the member function gets invoked for the instance of the class.
otherwise, if t1 does not satisfy the previous items,
then INVOKE(f, t1, t2, ..., tN) is equivalent to
((*t1).*f)(t2, ..., tN).
Otherwise: cross your fingers, hope that the first parameter is a pointer to an instance of the member function's class, and invoke it via the given pointer (hoping against hope that it's a pointer, or something that pretends that it's a pointer).
Final results: either one is equivalent to the other.
why func and func2 both can call member function Foo::print_add correctly?
They can because standard says that they can.
From what I know, ‘this’ pointer is passed as a hidden argument
From the language perspective, there is no "hidden pointer passed". A function is called "on" an object and this is simply a keyword that produces pointer to that object. Considering the notation not_a_pointer.member_function(other_arguments) there appears to be no pointer being passed syntactically.
Underneath the languge, from the implementation perspective (i.e. where "hidden" things are passed), there isn't a difference in the code that references and pointers produce. They're just memory addresses.
As an aside: It would be much more convenient if this produced a reference rather than a pointer. To my understanding, member functions and this were added to the language before references were added, so at that point reference wasn't an option. And afterwards, such change to the language would be backward incompatible.
I recently started adding async support to a library I'm working on, but I hit a slight problem. I started off with something like this (full context later):
return executeRequest<int>(false, d, &callback, false);
That was before adding async support. I attempted to change it to:
return std::async(std::launch::async, &X::executeRequest<int>, this, false, d, &callback, false);
But it failed to compile.
MCVE:
#include <iostream>
#include <future>
int callback(const int& t) {
std::cout << t << std::endl;
return t;
}
class RequestData {
private:
int x;
public:
int& getX() {
return x;
}
};
class X {
public:
template <typename T>
T executeRequest(bool method, RequestData& requestData,
std::function<T(const int&)> parser, bool write) {
int ref = 42;
std::cout << requestData.getX() << std::endl;
return parser(ref);
}
int nonAsync() {
// Compiles
RequestData d;
return this->executeRequest<int>(false, d, &callback, false);
}
std::future<int> getComments() {
RequestData d;
// Doesn't compile
return std::async(std::launch::async, &X::executeRequest<int>, this, false, d, &callback, false);
}
};
int main() {
X x;
auto fut = x.getComments();
std::cout << "end: " << fut.get() << std::endl;
}
And it fails with:
In file included from main.cpp:2:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/future:38:
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/functional:1505:56: error: no type named 'type' in 'std::result_of<std::_Mem_fn<int (X::*)(bool, RequestData &, std::function<int (const int &)>, bool)> (X *, bool, RequestData, int (*)(const int &), bool)>'
typedef typename result_of<_Callable(_Args...)>::type result_type;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/future:1709:49: note: in instantiation of template class 'std::_Bind_simple<std::_Mem_fn<int (X::*)(bool, RequestData &, std::function<int (const int &)>, bool)> (X *, bool, RequestData, int (*)(const int &), bool)>' requested here
__state = __future_base::_S_make_async_state(std::__bind_simple(
^
main.cpp:33:25: note: in instantiation of function template specialization 'std::async<int (X::*)(bool, RequestData &, std::function<int (const int &)>, bool), X *, bool, RequestData &, int (*)(const int &), bool>' requested here
return std::async(std::launch::async, &X::executeRequest<int>, this, false, d, &callback, false);
^
In file included from main.cpp:2:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/future:38:
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/functional:1525:50: error: no type named 'type' in 'std::result_of<std::_Mem_fn<int (X::*)(bool, RequestData &, std::function<int (const int &)>, bool)> (X *, bool, RequestData, int (*)(const int &), bool)>'
typename result_of<_Callable(_Args...)>::type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
2 errors generated.
Live example.
The only actual difference between the two (at least that I can see visibly) is that I need to explicitly pass this, because I'm referencing a member function
I played a little around with it, and managed to find that if I replace it with a const RequestData&, it's suddenly allowed. But it instead results in issues elsewhere, because the getter isn't const. At least from what I could find, I need to make it a const function, which is fine for the getter itself, but I also have some setters meaning I can't go with that.
Anyway, I figured I could try std::bind instead. I replaced the async call with:
auto func = std::bind(&X::executeRequest<int>, this, false, d, &callback, false);
return std::async(std::launch::async, func);
And, for some reason, it worked.
The thing that confuses me here, is that it uses the same arguments both times (all three times if you count the non-async variant), and takes the this argument into consideration, given the function I'm calling is a member function.
I dug deeper, and found some alternative solutions (referencing std::thread though), that used std::ref. I know std::async runs std::thread under the hood, so I dug up the documentation:
The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref or std::cref).
(emphasis mine)
That makes sense, and explains why it failed. I assume std::async is limited by this as well, and explains why it failed.
However, digging up std::bind:
The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.
(emphasis mine)
I don't use std::ref (or if I replace with a const, std::cref) in either, but at least if I understood the documentation right, both of these should fail to compile. The example on cppreference.com also compiles without std::cref (tested in Coliru with Clang and C++ 17).
What's going on here?
If it matters, aside the coliru environment, I originally reproduced the issue in Docker, running Ubuntu 18.04 with Clang 8.0.1 (64 bit). Compiled against C++ 17 in both cases.
There is some slight differences in the standard. For std::bind:
Requires: is_constructible_v<FD, F> shall be true.
For each Ti in BoundArgs, is_constructible_v<TDi, Ti> shall be true.
INVOKE(fd, w1, w2, …, wN) ([func.require]) shall be a valid expression for some values w1, w2, …, wN, where N has the value sizeof...(bound_args).
The cv-qualifiers cv of the call wrapper g, as specified below, shall be neither volatile nor const volatile.
Returns: An argument forwarding call wrapper g ([func.require]).
The effect of g(u1, u2, …, uM) shall be
INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), …, std::forward<VN>(vN))
Where v1, ..., vN have specific types. In your case, what matters is that the stored variable corresponding to d has type std::decay_t<RequestData&> which is RequestData. In this case, you can easily call executeRequest<int> with an lvalue RequestData.
The requirements for std::async are much stronger:
Requires: F and each Ti in Args shall satisfy the Cpp17MoveConstructible requirements, and
INVOKE(decay-copy(std::forward<F>(f)),
decay-copy(std::forward<Args>(args))...) // see [func.require], [thread.thread.constr]
The huge difference is decay-copy. For d, you get the following:
decay-copy(std::forward<RequestData&>(d))
Which is a call to the decay-copy function (exposition only), whose return type is std::decay_t<RequestData&>, so RequestData, which is why the compilation fails.
Note that if you used std::ref, the behavior would be undefined since the lifetime of d may ends before the call to executeRequest.
The thing that confuses me here, is that it uses the same arguments both times
But it doesn't forward them the same both times. When calling the async version, the callable is invoked as if by calling:
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);
The arguments are turned into something akin to temporaries! For this reason, the reference RequestData& requestData cannot bind to its argument. A const reference, a rvalue reference or a plain by value argument would work (as in, bind) here, but a non-const lvalue reference cannot.
std::bind does its invocation differently. It too stores copies, but "the ordinary stored argument arg is passed to the invokable object as lvalue argument[sic]", with the cv qualification of the argument derived from the bind object itself. Since std::bind creates a non-const bind object, the invokable is provided with a non-const lvalue for requestData. The reference binds to that happily.
When I run this code with libc++:
struct foo
{
foo(int x) : x(x)
{}
int x;
};
int main()
{
const auto select_x = std::mem_fn(&foo::x);
foo f(1);
printf("%i\n", select_x(f));
}
I get an error like this:
mem_fn.cpp:16:20: error: no matching function for call to object of type
'const std::__1::__mem_fn<int foo::*>'
printf("%i\n", select_x(f));
^~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:1224:11: note:
candidate function not viable: 'this' argument has type 'const
std::__1::__mem_fn<int foo::*>', but method is not marked const
operator() (_ArgTypes&&... __args)
^
It seems that libc++ is missing a const overload. It work using libstdc++. Is this a bug in libc++? Or does the standard require a const overload?
I don't see the standard specifying anything about the const-callability of the wrapper returned by mem_fn. From [func.memfn] (quoting N4140):
template<class R, class T> unspecified mem_fn(R T::* pm);
1 Returns: A simple call wrapper (20.9.1) fn such that the
expression fn(t, a2, ..., aN) is equivalent to INVOKE (pm, t, a2, ..., aN)
(20.9.2). fn shall have a nested type result_type that
is a synonym for the return type of pm when pm is a pointer to
member function.
2 The simple call wrapper shall define two nested types named
argument_type and result_type as synonyms for cv T* and Ret,
respectively, when pm is a pointer to member function with
cv-qualifier cv and taking no arguments, where Ret is pm’s
return type.
3 The simple call wrapper shall define three nested types
named first_argument_type, second_argument_type, and
result_type as synonyms for cv T*, T1, and Ret, respectively,
when pm is a pointer to member function with cv-qualifier cv and
taking one argument of type T1, where Ret is pm’s return type.
4 Throws: Nothing.
The reference to [func.def] (though simple call wrapper is actually defined in [func.require]) is equally unilluminating. This pretty much only guarantees that whatever it returns is usable directly.
As a quality of implementation issue, though, I see no reason why the wrapper shouldn't be const-callable, and it looks like this actually has been fixed over a year ago. You might want to try updating your XCode to see if you can pick up a more recent version of the library.
I'm trying to create a factory function that would return boost::interprocess::unique_ptr. Here's an example:
#include <boost/interprocess/smart_ptr/unique_ptr.hpp>
using namespace boost::interprocess;
class my_class {
public:
my_class() {}
};
struct my_class_deleter {
void operator()(my_class *p) {}
};
typedef unique_ptr<my_class, my_class_deleter> uptr;
uptr create() {
return uptr();
}
int main() {
uptr x;
x = create();
return 0;
}
The problem is that gcc fails to compile the above code saying:
main.cpp:22: error: ambiguous overload for ‘operator=’ in ‘x = create()()’
../../boost_latest/boost/interprocess/smart_ptr/unique_ptr.hpp:211: note: candidates are: boost::interprocess::unique_ptr<T, D>& boost::interprocess::unique_ptr<T, D>::operator=(boost::rv<boost::interprocess::unique_ptr<T, D> >&) [with T = my_class, D = my_class_deleter]
../../boost_latest/boost/interprocess/smart_ptr/unique_ptr.hpp:249: note: boost::interprocess::unique_ptr<T, D>& boost::interprocess::unique_ptr<T, D>::operator=(int boost::interprocess::unique_ptr<T, D>::nat::*) [with T = my_class, D = my_class_deleter]
When I change
x = create();
to
x = boost::move(create());
then gcc says:
main.cpp:22: error: invalid initialization of non-const reference of type ‘uptr&’ from a temporary of type ‘uptr’
../../boost_latest/boost/move/move.hpp:330: error: in passing argument 1 of ‘typename boost::move_detail::enable_if<boost::has_move_emulation_enabled<T>, boost::rv<T>&>::type boost::move(T&) [with T = uptr]’
Am I doing something wrong?
Interestingly, when I do:
uptr x2 = create();
the code compiles without any issues.
BTW: I use gcc v4.4.3 and Boost v1.51.0.
UPDATE:
I've been able to overcome this issue by using the following snippet:
x = static_cast<boost::rv<uptr>&>(create());
The above cast is based on the first version of ambiguous overload for operator= mentioned in the original question. The second one (operator=(int boost::interprocess::unique_ptr<T, D>::nat::*) is probably provided by the implementation to emulate std::unique_ptr::operator=(nullptr_t), which as a matter of fact resets the unique_ptr. It turns out, it also makes operator= ambiguous.
Unfortunately, using the above-mentioned static_cast<>() makes using my factory too much complicated.
One way to solve this problem would be to remove the second overload for operator=, as one can always explicitly call unique_ptr::reset().
Still, I'm wondering if and how boost::move() could help me with this issue.
This turned out to be a bug in the implementation of boost::interprocess:unique_ptr.
I reported it to the maintainer of the library (ticket #7598).
The bug has been fixed and the fix will be available in Boost v1.54.0.
Not sure if it meets your use-case but rather than using unique_ptr, can't you make my_class a moveable type (with Boost::Move) and do things by value using Boost ValueFactory for the factory interface?
I have class with a member function that takes a default argument.
struct Class
{
void member(int n = 0)
{}
};
By means of std::tr1::mem_fn I can invoke it:
Class object;
std::tr1::mem_fn(&Class::member)(object,10);
That said, if I want to invoke the callable member on the object with the default argument, what's the correct syntax?
std::tr1::mem_fn(&Class::member)(object); // This does not work
g++ complains with the following error:
test.cc:17: error: no match for call to ‘(std::tr1::_Mem_fn<void (Class::*)(int)>) (Class&)’
/usr/include/c++/4.3/tr1_impl/functional:551: note: candidates are: _Res std::tr1::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class&, _ArgTypes ...) const [with _Res = void, _Class = Class, _ArgTypes = int]
/usr/include/c++/4.3/tr1_impl/functional:556: note: _Res std::tr1::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class*, _ArgTypes ...) const [with _Res = void, _Class = Class, _ArgTypes = int]
Still, the I have the same problem when Class::member is overloaded by members that takes different arguments...
Default functions are bound at call time, but can't be bound into any sort of wrapper implicitly, because of the way they are implemented. When you pass &Class::member, mem_fn only sees a void (Class::*)(int), and can't see the default argument. Using tr1::bind, you can bind the default argument explictly: std::tr1::bind(&Class::member, 0) or you can use it as you would mem_fn, but you can't do both in one object. You would have to write your own wrapper class for that.
As for overloads, you will have to explicitly specify the template arguments for mem_fn so the right function pointer is picked as in mem_fn<void(int)>(&Class::member).
The reason is that any default arguments do not change the function type of a function.
mem_fn has no way to know the function only requires 1 argument, or that the functions' second argument is optional, since all the knowledge it gets is given to it by the type of &Class::member (which stays void(Class::*)(int)) . It therefor requires an integer as the second argument.
If you want to pass the address of a member function overloaded, you have to cast to the right member function pointer type:
static_cast<void(Class::*)()>(&Class::member) instead of just &Class::member, so the compiler has a context to figure out which address is to be taken.
Edit: coppro has a nicer solution how to provide context: std::tr1::mem_fn<void()>(&Class::member)