是否可以使用基类构造函数创建派生类的指针而不修改派生类的布局?



考虑这个布局:

#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_collectiontask被修改,但没有任何派生类?

如果问题的所有者可以接受使用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().而不是总是使用->

相关内容

  • 没有找到相关文章

最新更新