可变参数模板构造函数在元组中混合左值和右值
Variadic template constructor mixing lvalues and rvalues in a tuple
我想推迟一大堆 classes 的实例化,其中只有一小部分会被实例化。为此,我尝试在可变 class 中捕获构造函数的参数,并将它们存储在元组中。
我的问题是我只成功地存储了引用或副本。但是,我需要同时将这两种类型转发给构造函数,否则我会遇到范围问题。
#include <iostream>
#include <utility>
#include <tuple>
#include <vector>
// Interface of the classes which instanciation is being deferred.
struct OpItf {
virtual void operator()() = 0;
virtual ~OpItf() {}
};
struct Operator : public OpItf {
int& _i;
int _j;
// Need both a lvalue reference and a rvalue.
Operator(int& i, int j) : _i(i), _j(j) {}
void operator()() {std::cout << _i << " " << _j << std::endl;}
virtual ~OpItf() {}
};
// The interface of the class managing the actual instanciation.
template<class Itf>
struct ForgeItf {
virtual Itf& instanciate() = 0;
virtual ~ForgeItf() {}
};
template<class Itf, class Op, typename... Args>
struct Forge : public ForgeItf<Itf> {
std::tuple<Args&&...> _args;
Itf* _instanciated;
Forge(Args&&... args) :
_args(std::forward<Args>(args)...),
_instanciated(nullptr)
{ }
Itf& instanciate()
{
if(_instanciated) {
delete _instanciated;
}
_instanciated = op_constructor(_args);
return *_instanciated;
}
virtual ~Forge() { delete _instanciated; }
template<class T>
Op* op_constructor(T& args) { return new Op(std::make_from_tuple<Op>(args)); }
};
// A container of forges.
template<class Itf>
struct Foundry : std::vector< ForgeItf<Itf>* > {
template<class Op, typename... Args>
void add(Args&&... args)
{
auto pfo = new Forge<Itf,Op,Args&&...>(std::forward<Args>(args)...);
this->push_back(pfo);
}
virtual ~Foundry() { for(auto p : *this) { delete p; } }
};
int main() {
Foundry<OpItf> foundry;
int iref = 1;
// Store the constructors parameters.
for(int j=0; j<3; ++j) {
foundry.add< Operator >(iref,j);
}
// Change the referenced parameter before instanciations.
iref = 2;
// Actually instanciate.
for(auto& forge : foundry ) {
auto& op = forge->instanciate();
op();
}
}
使用右值 Args...
,构造函数获取值,但对 i
的引用(错误地)未更改:
1 0
1 1
1 2
使用转发引用 Args&&...
,构造函数正确获取引用,但 j
:
的副本超出范围
2 1228009304
2 1228009304
2 1228009304
保持临时值有效的唯一方法是通过复制将它们存储到元组中。否则,通过存储引用,您会将它们作为 dangling.
您可以使用 reference_wrapper
来存储 i
作为参考。
变化:
foundry.add< Operator >(std::ref(iref),j); // wrap i into reference_wrapper
---
auto pfo = new Forge<Itf,Op,std::decay_t<Args>...>(std::forward<Args>(args)...);
使用std::decay_t<Args>...
按值存储所有包参数(复制reference_wrapper
的操作很便宜)。
template<class Itf, class Op, typename... Args>
struct Forge : public ForgeItf<Itf> {
std::tuple<Args...> _args;
Itf* _instanciated;
template<class ... Args2>
Forge(Args2&&... args2) :
_args(std::forward<Args2>(args2)...),
_instanciated(nullptr)
{ }
因为Args
已经衰减,只存储:tuple<Args...>
并让构造函数采用转发引用以避免调用构造函数时出现冗余副本。
作为输出:
2 1
2 2
2 3
我想推迟一大堆 classes 的实例化,其中只有一小部分会被实例化。为此,我尝试在可变 class 中捕获构造函数的参数,并将它们存储在元组中。
我的问题是我只成功地存储了引用或副本。但是,我需要同时将这两种类型转发给构造函数,否则我会遇到范围问题。
#include <iostream>
#include <utility>
#include <tuple>
#include <vector>
// Interface of the classes which instanciation is being deferred.
struct OpItf {
virtual void operator()() = 0;
virtual ~OpItf() {}
};
struct Operator : public OpItf {
int& _i;
int _j;
// Need both a lvalue reference and a rvalue.
Operator(int& i, int j) : _i(i), _j(j) {}
void operator()() {std::cout << _i << " " << _j << std::endl;}
virtual ~OpItf() {}
};
// The interface of the class managing the actual instanciation.
template<class Itf>
struct ForgeItf {
virtual Itf& instanciate() = 0;
virtual ~ForgeItf() {}
};
template<class Itf, class Op, typename... Args>
struct Forge : public ForgeItf<Itf> {
std::tuple<Args&&...> _args;
Itf* _instanciated;
Forge(Args&&... args) :
_args(std::forward<Args>(args)...),
_instanciated(nullptr)
{ }
Itf& instanciate()
{
if(_instanciated) {
delete _instanciated;
}
_instanciated = op_constructor(_args);
return *_instanciated;
}
virtual ~Forge() { delete _instanciated; }
template<class T>
Op* op_constructor(T& args) { return new Op(std::make_from_tuple<Op>(args)); }
};
// A container of forges.
template<class Itf>
struct Foundry : std::vector< ForgeItf<Itf>* > {
template<class Op, typename... Args>
void add(Args&&... args)
{
auto pfo = new Forge<Itf,Op,Args&&...>(std::forward<Args>(args)...);
this->push_back(pfo);
}
virtual ~Foundry() { for(auto p : *this) { delete p; } }
};
int main() {
Foundry<OpItf> foundry;
int iref = 1;
// Store the constructors parameters.
for(int j=0; j<3; ++j) {
foundry.add< Operator >(iref,j);
}
// Change the referenced parameter before instanciations.
iref = 2;
// Actually instanciate.
for(auto& forge : foundry ) {
auto& op = forge->instanciate();
op();
}
}
使用右值 Args...
,构造函数获取值,但对 i
的引用(错误地)未更改:
1 0
1 1
1 2
使用转发引用 Args&&...
,构造函数正确获取引用,但 j
:
2 1228009304
2 1228009304
2 1228009304
保持临时值有效的唯一方法是通过复制将它们存储到元组中。否则,通过存储引用,您会将它们作为 dangling.
您可以使用 reference_wrapper
来存储 i
作为参考。
变化:
foundry.add< Operator >(std::ref(iref),j); // wrap i into reference_wrapper
---
auto pfo = new Forge<Itf,Op,std::decay_t<Args>...>(std::forward<Args>(args)...);
使用std::decay_t<Args>...
按值存储所有包参数(复制reference_wrapper
的操作很便宜)。
template<class Itf, class Op, typename... Args>
struct Forge : public ForgeItf<Itf> {
std::tuple<Args...> _args;
Itf* _instanciated;
template<class ... Args2>
Forge(Args2&&... args2) :
_args(std::forward<Args2>(args2)...),
_instanciated(nullptr)
{ }
因为Args
已经衰减,只存储:tuple<Args...>
并让构造函数采用转发引用以避免调用构造函数时出现冗余副本。
作为输出:
2 1
2 2
2 3