防止早期对象破坏
Preventing early object destruction
在 C++ 中,如果我写
token make_token() { return token{}; }
然后如下使用
void use_token()
{
make_token();
// extra code
}
在不为变量分配标记的情况下,token
的析构函数会在执行额外代码之前触发。我怎样才能让析构函数只在函数结束时触发而不必创建变量?
注意:我想完全避免做变量。我知道我可以做 auto& t = make_token()
或类似的事情,但我想通过 returning something (我不知道是什么)来避免这种情况立即触发析构函数。
为什么我想要这个: 基本上,在我的应用程序(一种编程语言的编译器)中,我有这些称为令牌的东西。令牌的构造函数可以放置 {
和缩进,然后其析构函数可以放置 }
和取消缩进。我认为按值设置 return 这些标记的函数是个好主意,但实际上我不想将它们分配给任何值,因为这些标记无用且没有功能。
为了减少混淆,我的 token
不是 一个词汇标记。我用作品 token
代替作品 cookie
。它意味着在构造函数中做一些事情,等到它的范围结束,然后在它的析构函数中做一些事情。而已。顺便说一下,如果我用 C# 写这个,我会写类似
using (make_token())
{
// my code here
}
它会按预期工作。但事实证明,如此简单的事情在 C++ 中很难。
是的。您可以使用常量引用。这在 C++ 中被称为最重要的 const,这是一个不为人知的特性。
操作方法如下:
void use_token()
{
const token& myToken = make_token();
// now myToken is alive until the end of this function.
}
但是你必须 return 严格按值才能工作(你在你提供的代码中这样做)。
不信的小伙伴请自己尝试一下再攻击post。
嗯,您可能认为您可以接收该函数返回的值;
void use_token()
{
auto nonsense = make_token();
// extra code
}
即便如此,您是否知道(C++17 之前的版本)...当 RVO 未发生时仍有可能调用两个析构函数?
按照所说的const
参考是最好的出路。
像这样:
make_token(),
[](){ /* extra stuff */ }();
确保事后洗手 :)
如果您能够使用 C++11 或更高版本,您可以编写类似以下内容的模板函数:
template <typename T, typename Functor>
void keep_alive(T&&, Functor f) {
f();
}
...
void use_token() {
keep_alive(make_token(), [&] {
// rest of body of function
});
}
在澄清需要它的原因后进行编辑:
对于创建标记以放入 { } 和缩进的特定用例,您可以创建一个专门命名的包装函数,以明确发生了什么:
template <typename Functor>
void make_indented_block(Functor f) {
auto indentToken = make_token();
f();
}
我感觉到你的痛苦,auto = make_token()
会有用。然而...
您可能遇到了 XY 问题。你不应该这样做吗:
with_token([&]{ ... });
即令牌 generator/constructor 采用 lambda?如果您不想在创建令牌的实际函数中将 lambda 中的 return
写入 return,这应该工作 。
另一种方法是,如果你只是想让别人'know the name',臭名昭著的 for 模式:
template<typename T>
struct Keeper
{
const T t;
char b;
Keeper(const T& t_)
: t(t_) {}
char* begin() { return &b; }
char* end() { return begin() + 1; }
};
template<typename T>
auto make_keeper(const T& t)
{
return Keeper<T>(t);
}
void f()
{
for (char c : make_keeper(make_token()))
{
// now try to name t or Keeper<T>(t) here
}
}
如果您愿意,您可以添加移动原理图和完善前向。
我们这里有 classical XY problem:
所以对于 C# 代码:
using (make_token())
{
// my code here
}
创建一个 class 令牌:
class token {
public:
token() { /* calling make_token(); */ }
~token() { /* destroying token */ }
};
然后使用它:
{
token tok;
// some stuff here
{
token tok;
// some other stuff here
}
}
所以
- 这种用法对于 C++ 开发人员来说很清楚,您的 C++ API 对他们来说也很容易使用。
- 你关于创建唯一变量名的问题的说法是错误的,如图所示你可以使用相同的名称。
- 你不必告诉任何人不要使用这个变量,因为它只有构造函数和析构函数。
如果有必要你可以把它放到宏里,但我个人觉得它更难用。
在 C++ 中,如果我写
token make_token() { return token{}; }
然后如下使用
void use_token()
{
make_token();
// extra code
}
在不为变量分配标记的情况下,token
的析构函数会在执行额外代码之前触发。我怎样才能让析构函数只在函数结束时触发而不必创建变量?
注意:我想完全避免做变量。我知道我可以做 auto& t = make_token()
或类似的事情,但我想通过 returning something (我不知道是什么)来避免这种情况立即触发析构函数。
为什么我想要这个: 基本上,在我的应用程序(一种编程语言的编译器)中,我有这些称为令牌的东西。令牌的构造函数可以放置 {
和缩进,然后其析构函数可以放置 }
和取消缩进。我认为按值设置 return 这些标记的函数是个好主意,但实际上我不想将它们分配给任何值,因为这些标记无用且没有功能。
为了减少混淆,我的 token
不是 一个词汇标记。我用作品 token
代替作品 cookie
。它意味着在构造函数中做一些事情,等到它的范围结束,然后在它的析构函数中做一些事情。而已。顺便说一下,如果我用 C# 写这个,我会写类似
using (make_token())
{
// my code here
}
它会按预期工作。但事实证明,如此简单的事情在 C++ 中很难。
是的。您可以使用常量引用。这在 C++ 中被称为最重要的 const,这是一个不为人知的特性。
操作方法如下:
void use_token()
{
const token& myToken = make_token();
// now myToken is alive until the end of this function.
}
但是你必须 return 严格按值才能工作(你在你提供的代码中这样做)。
不信的小伙伴请自己尝试一下再攻击post。
嗯,您可能认为您可以接收该函数返回的值;
void use_token()
{
auto nonsense = make_token();
// extra code
}
即便如此,您是否知道(C++17 之前的版本)...当 RVO 未发生时仍有可能调用两个析构函数?
按照const
参考是最好的出路。
像这样:
make_token(),
[](){ /* extra stuff */ }();
确保事后洗手 :)
如果您能够使用 C++11 或更高版本,您可以编写类似以下内容的模板函数:
template <typename T, typename Functor>
void keep_alive(T&&, Functor f) {
f();
}
...
void use_token() {
keep_alive(make_token(), [&] {
// rest of body of function
});
}
在澄清需要它的原因后进行编辑:
对于创建标记以放入 { } 和缩进的特定用例,您可以创建一个专门命名的包装函数,以明确发生了什么:
template <typename Functor>
void make_indented_block(Functor f) {
auto indentToken = make_token();
f();
}
我感觉到你的痛苦,auto = make_token()
会有用。然而...
您可能遇到了 XY 问题。你不应该这样做吗:
with_token([&]{ ... });
即令牌 generator/constructor 采用 lambda?如果您不想在创建令牌的实际函数中将 lambda 中的 return
写入 return,这应该工作 。
另一种方法是,如果你只是想让别人'know the name',臭名昭著的 for 模式:
template<typename T>
struct Keeper
{
const T t;
char b;
Keeper(const T& t_)
: t(t_) {}
char* begin() { return &b; }
char* end() { return begin() + 1; }
};
template<typename T>
auto make_keeper(const T& t)
{
return Keeper<T>(t);
}
void f()
{
for (char c : make_keeper(make_token()))
{
// now try to name t or Keeper<T>(t) here
}
}
如果您愿意,您可以添加移动原理图和完善前向。
我们这里有 classical XY problem:
所以对于 C# 代码:
using (make_token())
{
// my code here
}
创建一个 class 令牌:
class token {
public:
token() { /* calling make_token(); */ }
~token() { /* destroying token */ }
};
然后使用它:
{
token tok;
// some stuff here
{
token tok;
// some other stuff here
}
}
所以
- 这种用法对于 C++ 开发人员来说很清楚,您的 C++ API 对他们来说也很容易使用。
- 你关于创建唯一变量名的问题的说法是错误的,如图所示你可以使用相同的名称。
- 你不必告诉任何人不要使用这个变量,因为它只有构造函数和析构函数。
如果有必要你可以把它放到宏里,但我个人觉得它更难用。