通过重新绑定变量来重用泛型类型的堆内存。
我想做的:
template <typename T> //unconstrained type
//...
{
//in some complex procedural logic
T rebindable = .../*r-value*/;
//...
//some procedure dependent case:
T rebindable = .../*different r-value*/; //rebind (I am aware this does not work)
}
为什么这是一个值得问的问题:
许多过程算法在其作用域内维护并持续地对内存进行操作。有时在逻辑上有必要完全替换这些值中的一个,这在编程上意味着解构和构造,可能需要不确定次数(重新绑定)。通常,这可以通过利用迭代来克服,例如,在可能不可赋值的数字类型上实现智能对分根查找器。
然而,一些更复杂的过程算法可能不容易用函数编写,特别是当重绑定是由过程状态决定的时候。
因此以编程方式实现重绑定对于泛型算法是非常有用的。
不满意答案(这绝不是对任何人或他们的答案的攻击)
Unique_ptr:
template <typename T> //still unconstrained (which is a plus)
//...
{
auto ptr = std::unique_ptr<T>{.../*dangling reference (T*)*/};
//...
ptr = std::unique_ptr<T>{.../*different dangling reference*/};
}
这解决了不限制类型的问题,但是使用的内存不是堆内存。
任务:
template <typename T>
requires std::assignable_from<T, T> //type constraint!
//...
{
T assignable = .../*r-value*/;
//...
assignable = .../*different r-value*/;
}
虽然这是在堆上,但它的代价是限制类型。
新位置:
template <std::copy_constructible T> //type constraint!
//...
{
T assignable = .../*r-value*/;
//...
assignable.~T();
new (&assignable) T {.../*different r-value*/};
}
通过解构和重新构造赋值new,值被反弹,但是这仍然有一个相关的类型约束。
如果你愿意为堆栈上额外的布尔值付费,那么像std::optional
这样的类型可以做到这一点。
template <typename T> //unconstrained type
void func()
{
//in some complex procedural logic
std::optional<T> rebindable;
rebindable.emplace(/*r-value*/);
//...
//some procedure dependent case:
rebindable.reset();
rebindable.emplace(/*different r-value*/);
}
我认为你需要它是可移动的,因为你必须通过放置函数。
使用new
重新绑定最后一个使用位置new的例子不需要关联的类型约束!
表达式T t = r-value;
不调用构造函数,同样,表达式new (&t) T {r-value};
也不调用构造函数。
位置new绕过带有r值的构造函数
下面是这个行为的一个例子:(在GCC上编译)
#include<iostream>
#include<utility>
struct C
{
int n;
static C make_new(int n) {return C{n};}
~C() = default;
C(int n): n(n) {}
C(const C &) = delete;
C(C&&) = delete;
};
template <typename T>
void inline rebind(T& re_bin_ref, T&& val) {
re_bin_ref.~T();
new (&re_bin_ref) T {val};
}
template <typename T, typename... TArgs>
void inline emplace_rebind(T& re_bin_ref, TArgs&&... args) {
re_bin_ref.~T();
new (&re_bin_ref) T {std::forward<decltype(args)>(args)...};
}
template <typename T>
void inline procedural_rebind(T& re_bin_ref, auto f) {
re_bin_ref.~T();
new (&re_bin_ref) T { f() };
}
int main()
{
std::cout << "nREBINDING:n";
C re_bin = C::make_new(0);
std::cout << "init: " << re_bin.n << std::endl;
//...
re_bin.~C();
new (&re_bin) C {C{1}};
std::cout << "pr-value: " << re_bin.n << std::endl;
//...
//re_bin.~C();
//new (&re_bin) C {std::move<C>(C::make_new(2))}; //requires C&&
//std::cout << "x-value: " << re_bin.n << std::endl;
//...
//rebind(re_bin, C::make_new(3)); //requires C&&
//std::cout << "explicit rebind: " << re_bin.n << std::endl;
//...
emplace_rebind(re_bin, 4);
std::cout << "explicit emplace rebind: " << re_bin.n << std::endl;
//...
procedural_rebind(re_bin, [](){return C{5};});
std::cout << "explicit pr-value procedural rebind: " << re_bin.n << std::endl;
return 0;
}
GodBolt的完整文件:https://godbolt.org/z/cz7Y6qzbe