Reputation: 17
I was intend to pass a reference of a variable into a member function pointer, which is a parameter of another varadic template function to invoke any kind of member function of a class, but from the printing result, it is not passed by reference, but just by value.
#include <functional>
#include <iostream>
#include <memory>
template <class WorkerType> class Delegate {
public:
template <typename... Args>
using WrkFunc = void (WorkerType::*)(Args... args);
explicit Delegate(WorkerType &wrk) : m_worker(&wrk) {}
template <typename... Args>
void workerDo(WrkFunc<Args...> func, Args &&... args) {
auto fn = std::bind(func, m_worker.get(), std::forward<Args>(args)...);
fn();
}
private:
std::shared_ptr<WorkerType> m_worker;
};
class SomeWorker {
public:
SomeWorker() = default;
void doSomething(int &a) {
a = 1000;
std::cout << "2# address: " << &a << ", value: " << a << std::endl;
}
};
int main() {
SomeWorker wrk;
Delegate<SomeWorker> del(wrk);
int a = 0;
std::cout << "1# address: " << &a << ", value: " << a << std::endl;
del.workerDo(&SomeWorker::doSomething, a);
std::cout << "3# address: " << &a << ", value: " << a << std::endl;
return 0;
}
what I expected result like this:
1# address: 0x7fffc1dc621c, value: 0
2# address: 0x7fffc1dc621c, value: 1000
3# address: 0x7fffc1dc621c, value: 1000
but the actual result is:
1# address: 0x7fffc1dc621c, value: 0
2# address: 0x7fffc1dc61d0, value: 1000
3# address: 0x7fffc1dc621c, value: 0
Upvotes: 0
Views: 64
Reputation: 1202
At first, your Delegate
class's constructor is completely broken. That is a wrong usage of std::shared_ptr
. You should fix like below:
// define
explicit Delegate(std::shared_ptr<WorkerType> wrk) : m_worker(std::move(wrk)) {}
//call
Delegate<SomeWorker> del(std::make_shared<SomeWorker>());
To pass a object lvalue reference to std::bind
, you must use std::ref
.
How to bind function to an object by reference?
When you pass std::ref
result to workerDo
, WrkFunc
will disturb calling.
So, you need to rewrite type check using std::is_invocable_r_v
#include <functional>
#include <iostream>
#include <memory>
#include <type_traits>
template <class WorkerType> class Delegate {
public:
explicit Delegate(std::shared_ptr<WorkerType> wrk) : m_worker(std::move(wrk)) {}
template <typename Func, typename ...Args, std::enable_if_t<std::is_invocable_r_v<void, Func, WorkerType, Args...>, std::nullptr_t> = nullptr>
void workerDo(Func func, Args && ...args) {
auto fn = std::bind(func, m_worker.get(), std::forward<Args>(args)...);
fn();
}
private:
std::shared_ptr<WorkerType> m_worker;
};
class SomeWorker {
public:
SomeWorker() = default;
void doSomething(int &a) {
a = 1000;
std::cout << "2# address: " << &a << ", value: " << a << std::endl;
}
};
int main() {
Delegate<SomeWorker> del(std::make_shared<SomeWorker>());
int a = 0;
std::cout << "1# address: " << &a << ", value: " << a << std::endl;
del.workerDo(&SomeWorker::doSomething, std::ref(a));
std::cout << "3# address: " << &a << ", value: " << a << std::endl;
return 0;
}
https://wandbox.org/permlink/fnBThXw5Uh72JeGQ
is there any method to avoid using std::ref everywhere call the workerDo ?
using wrapper function like below will solve.
template <class T, class U, std::enable_if_t<
(std::is_lvalue_reference_v<T> ? std::is_lvalue_reference_v<U> : true) &&
std::is_convertible_v<std::remove_reference_t<U>*, std::remove_reference_t<T>*>,
std::nullptr_t
> = nullptr>
inline decltype(auto) forward_or_construct_reference_wrapper(U&& u)
{
if constexpr(std::is_lvalue_reference_v<T>) {
return std::reference_wrapper{std::forward<T>(u)};
}
else {
return static_cast<T&&>(u);
}
}
https://wandbox.org/permlink/wli9Dh9vXSjzBWMA
Upvotes: 1