C++:重新绑定泛型类型的变量
C++ : rebind variable of generic type
通过重新绑定变量为泛型重新使用堆内存。
我想做的事情:
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)
}
为什么这是一个值得问的问题:
许多过程算法在其范围内维护并持续对内存进行操作。
有时完全替换这些值之一在逻辑上是必要的,在编程上这意味着解构和构造,可能是不确定的次数(重新绑定)。
通常这会通过利用迭代来克服,例如 smart-bisection root finder 对可能无法分配的数字类型的实现。
然而,一些更复杂的过程算法可能无法简单地编写功能,特别是如果重新绑定是由过程状态决定的。
因此,以编程方式实现重新绑定对于通用算法可能非常有用。
不满意的答案
(这绝不是攻击任何人或他们的答案)
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 值是反弹的,但是这仍然有一个关联的类型约束。
使用新位置重新绑定
最后一个使用 placement new 的例子不需要关联类型约束!
表达式T t = r-value;
不调用构造函数,同理表达式new (&t) T {r-value};
也不调用构造函数
placement 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
如果您愿意为堆栈上的额外布尔值付费,像 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*/);
}
我认为你需要它是可移动的,因为你必须通过 emplace 函数。
通过重新绑定变量为泛型重新使用堆内存。
我想做的事情:
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)
}
为什么这是一个值得问的问题:
许多过程算法在其范围内维护并持续对内存进行操作。 有时完全替换这些值之一在逻辑上是必要的,在编程上这意味着解构和构造,可能是不确定的次数(重新绑定)。
通常这会通过利用迭代来克服,例如 smart-bisection root finder 对可能无法分配的数字类型的实现。
然而,一些更复杂的过程算法可能无法简单地编写功能,特别是如果重新绑定是由过程状态决定的。
因此,以编程方式实现重新绑定对于通用算法可能非常有用。
不满意的答案
(这绝不是攻击任何人或他们的答案)
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 值是反弹的,但是这仍然有一个关联的类型约束。
使用新位置重新绑定
最后一个使用 placement new 的例子不需要关联类型约束!
表达式T t = r-value;
不调用构造函数,同理表达式new (&t) T {r-value};
也不调用构造函数
placement 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
如果您愿意为堆栈上的额外布尔值付费,像 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*/);
}
我认为你需要它是可移动的,因为你必须通过 emplace 函数。