考虑这个布局:
#include <memory>
#include <vector>
struct data{};
struct task {
data* data_ptr = nullptr;
virtual void work() = 0;
};
struct special_task : task {
void work() override { /*work with the data*/ }
};
atask
对数据有访问权限。派生类必须实现它们各自的work()
例程。
现在task_collection
存储数据和任务指针的向量,派生类的实例可以添加到该向量:
struct task_collection {
data data;
std::vector<std::unique_ptr<task>> tasks;
template<typename T>
void add() {
this->tasks.push_back(std::make_unique<T>());
this->tasks.back()->data_ptr = &this->data;
}
};
int main() {
task_collection t;
t.add<special_task>();
}
现在工作得很好。然而,我想知道我是否可以用参考取代data* data_ptr
,因为它在这里似乎更合适,它也用.
s取代了许多->
然而,如果不改变special_task
的布局,似乎不可能实现这一点,因为引用需要在task
的基类中有一个构造函数,派生类会忽略它们的基构造函数:
struct task {
task(data& data) : data_ref(data){}
data& data_ref;
virtual void work() = 0;
};
struct special_task : task {
void work() override { /*work with the data*/ }
};
struct task_collection {
data data;
std::vector<std::unique_ptr<task>> tasks;
template<typename T>
void add() {
this->tasks.push_back(std::make_unique<T>(this->data)); //Error!
}
};
给出如下错误:
Error C2664 'special_task::special_task(const special_task &)': cannot convert argument 1 from 'data' to 'const special_task &'
我知道这可以"解决"。通过添加
using task::task;
到每个派生类,因为现在它找到了合适的构造函数。但这并不是一个真正的解决方案,因为可能有数百个派生类,由多个人编写。如果缺少一个using task::task;
实例,可能会引起头痛。额外的代码行也抵消了使用引用而不是指针所带来的好处。
那么有没有办法实现task::data_ref
作为参考,只有task_collection
或task
被修改,但没有任何派生类?
如果问题的所有者可以接受使用get()
而不是.
访问数据,他可以使用std::reference_wrapper而不是原始引用来解决重绑定问题。
#include <memory>
#include <vector>
#include <functional>
struct data{};
static data empty_data;
struct task {
std::reference_wrapper<data> data_ptr = empty_data;
virtual void work() = 0;
};
struct special_task : task {
void work() override { /*work with the data*/ }
};
struct task_collection {
data data;
std::vector<std::unique_ptr<task>> tasks;
template<typename T>
void add() {
this->tasks.push_back(std::make_unique<T>());
this->tasks.back()->data_ptr = std::ref(this->data);
}
};
int main()
{
task_collection t;
t.add<special_task>();
}
对于使用数据,例如,您可以像下面
那样操作data temp_data = t.tasks.back()->data_ptr;
在如何正确使用std::reference_wrapper中可以看到,std::reference_wrapper类实现了一个隐式转换操作符到T&:
constexpr operator T& () const noexcept;
因此,当需要T(或T&)时调用隐式操作符,而不需要使用get()
函数,例如
void f(some_type x);
// ...
std::reference_wrapper<some_type> x;
some_type y = x; // the implicit operator is called
f(x); // the implicit operator is called and the result goes to f.
所以有时候你必须使用.get().
而不是总是使用->