c++ - 为什么通过通用引用运算符 (&&) 将变量的引用传递给可变参数模板函数失败?
问题描述
我本来打算把一个变量的引用传递给一个成员函数指针,它是另一个varadic模板函数的参数来调用一个类的任何一种成员函数,但是从打印结果来看,它不是通过引用传递的,而是只是按价值。
#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;
}
我期望的结果是这样的:
1# 地址:0x7fffc1dc621c,值:0
2# 地址:0x7fffc1dc621c,值:1000
3# 地址:0x7fffc1dc621c,值:1000
但实际结果是:
1#地址:0x7fffc1dc621c,值:0
2#地址:0x7fffc1dc61d0,值:1000
3#地址:0x7fffc1dc621c,值:0
解决方案
起初,你的Delegate
类的构造函数完全被破坏了。那是错误的用法std::shared_ptr
。你应该像下面这样修复:
// define
explicit Delegate(std::shared_ptr<WorkerType> wrk) : m_worker(std::move(wrk)) {}
//call
Delegate<SomeWorker> del(std::make_shared<SomeWorker>());
要将对象左值引用传递给std::bind
,您必须使用std::ref
.
当您将std::ref
结果传递给 时workerDo
,WrkFunc
会打扰呼叫。
因此,您需要使用重写类型检查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
有什么方法可以避免在任何地方使用 std::ref 调用 workerDo 吗?
使用如下包装函数将解决。
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);
}
}
推荐阅读
- vb.net - 注册用VB.NET制作的com dll
- python - Word Mover 距离与 SpaCy 的非归一化结果
- python - 如何在列表中使用 HH:MM 格式的正则表达式过滤器获取唯一出现值的输出?
- angular - IONIC 4:无法读取未定义的属性“获取”
- r - data.table 中的“递归”自联接
- javascript - 两个几乎相同的 javascript;一个有效,另一个无效
- reactjs - 如何使用 Auth0 和 React 前端保护 S3 存储桶中的图像
- tensorflow - 如何在 keras 中实现这种损失
- reactjs - 模块解析失败:使用 webpack 构建反应时出现意外令牌 (6:16)
- swift - 迅速。timerLabel 中的超大标题