避免使用表达式模板#define
对于以下代码,“hello2”不会显示,因为第 3 行创建的临时字符串在第 4 行执行之前就消失了。在第 1 行中使用 #define 可以避免此问题,但是有没有办法在不使用 #define 的情况下避免此问题? (C++11 代码没问题)
#include <iostream>
#include <string>
class C
{
public:
C(const std::string& p_s) : s(p_s) {}
const std::string& s;
};
int main()
{
#define x1 C(std::string("hello1")) // Line 1
std::cout << x1.s << std::endl; // Line 2
const C& x2 = C(std::string("hello2")); // Line 3
std::cout << x2.s << std::endl; // Line 4
}
澄清:
请注意,我相信 Boost uBLAS 存储引用,这就是为什么我不想存储副本。如果你建议我按值存储,请解释为什么Boost uBLAS是错误的并且按值存储不会影响性能。
With the following code, "hello2" is not displayed as the temporary string created on Line 3 dies before Line 4 is executed. Using a #define as on Line 1 avoids this issue, but is there a way to avoid this issue without using #define? (C++11 code is okay)
#include <iostream>
#include <string>
class C
{
public:
C(const std::string& p_s) : s(p_s) {}
const std::string& s;
};
int main()
{
#define x1 C(std::string("hello1")) // Line 1
std::cout << x1.s << std::endl; // Line 2
const C& x2 = C(std::string("hello2")); // Line 3
std::cout << x2.s << std::endl; // Line 4
}
Clarification:
Note that I believe Boost uBLAS stores references, this is why I don't want to store a copy. If you suggest that I store by value, please explain why Boost uBLAS is wrong and storing by value will not affect performance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
按引用存储的表达式模板通常是为了性能而这样做,但需要注意的是,它们只能用作临时
模板取自 Boost.Proto 的文档(可用于创建表达式模板):
在您最初的示例中,这意味着您应该这样做:
这样
C
临时文件永远不会比std::string
临时文件寿命更长。或者,您可以将s
设为非引用成员,正如其他人指出的那样。既然你提到了 C++11,将来我希望表达式树按值存储,使用移动语义来避免昂贵的复制,并且像 std::reference_wrapper 这样的包装器仍然提供按引用存储的选项。这与
auto
配合得很好。代码的可能 C++11 版本:
这意味着像
C("hello").get()
这样的代码只会分配内存一次,但仍然可以很好地使用Expression templates that do store by reference typically do so for performance, but with the caveat they only be used as temporaries
Taken from the documentation of Boost.Proto (which can be used to create expression templates):
In your initial example this means that you should do:
That way the
C
temporary never outlives thestd::string
temporary. Alternatively you could makes
a non reference member as others pointed out.Since you mention C++11, in the future I expect expression trees to store by value, using move semantics to avoid expensive copying and wrappers like std::reference_wrapper to still give the option of storing by reference. This would play nicely with
auto
.A possible C++11 version of your code:
This would mean that code like
C("hello").get()
would only allocate memory once, but still play nice with是的。
将您的类定义为:(不存储引用)
存储副本!
演示: http://www.ideone.com/GpSa2
您的代码的问题是
std::string("hello2")
创建一个临时对象,只要您在C
的构造函数中,它就保持活动状态,之后临时对象被销毁,但您的对象x2.s
stills 指向它(死亡对象)。Yes.
Define your class as: (don't store the reference)
Store the copy!
Demo : http://www.ideone.com/GpSa2
The problem with your code is that
std::string("hello2")
creates a temporary, and it remains alive as long as you're in the constructor ofC
, and after that the temporary is destroyed but your objectx2.s
stills points to it (the dead object).编辑后:
通过引用存储有时危险且容易出错。仅当您 100% 确定变量引用在其死亡之前永远不会超出范围时才应执行此操作。
C++
string
非常优化。在您更改字符串值之前,所有值都将仅引用同一字符串。要测试它,您可以重载operator new (size_t)
并放置调试语句。对于同一字符串的多个副本,您将看到内存分配仅发生一次。您的类定义不应该通过引用存储,而应该通过值存储,
如果这个问题是为了一般意义(而不是特定于字符串),那么最好的方法是使用动态分配。
After your edit:
Storing by reference is dangerous and error prone sometimes. You should do it only when you are 100% sure that the variable reference will never go out of scope until its death.
C++
string
is very optimized. Until you change a string value, all will refer to the same string only. To test it, you can overloadoperator new (size_t)
and put a debug statement. For multiple copies of same string, you will see that the memory allocation will happen only once.You class definition should not be storing by reference, but by value as,
If this question is meant for general sense (not specific to string) then the best way is to use dynamic allocation.
如果不考虑 BLAS,表达式模板通常会大量使用您甚至不应该知道其存在的类型的临时对象。如果 Boost 在他们的引用中存储这样的引用,那么他们将遇到与您在这里看到的相同的问题。但是,只要这些临时对象仍然是临时的,并且用户不存储它们供以后使用,一切都很好,因为它们引用的临时对象与临时对象一样保持活动状态。技巧是当中间对象变成用户存储的最终对象时执行深层复制。您在这里跳过了最后一步。
简而言之,这是一个危险的举动,只要你的库的用户不做任何愚蠢的事情,这就是完全安全的。我不建议使用它,除非您有明确的需求,并且您很清楚后果。即便如此,可能还有更好的选择,我从未以任何严肃的身份使用过表达式模板。
顺便说一句,由于您标记了此 C++0x,
auto x = a + b;
似乎是您的代码的用户可以做的那些“愚蠢”的事情之一,使您的优化变得危险。Without looking at BLAS, expression templates typically make heavy use of temporary objects of types you aren't supposed to even know exists. If Boost is storing references like this within theirs, then they would suffer the same problem you see here. But as long as those temporary objects remain temporary, and the user doesnt store them for later, everything is fine because the temporaries they reference remain alive for as long as the temporary objects do. The trick is you perform a deep copy when the intermediate object is turned into the final object that the user stores. You've skipped this last step here.
In short, it's a dangerous move, which is perfectly safe as long as the user of your library doesn't do anything foolish. I wouldn't recommend making use of it unless you have a clear need, and you're well aware of the consequences. And even then, there might be a better alternative, I've never worked with expression templates in any serious capacity.
As an aside, since you tagged this C++0x,
auto x = a + b;
seems like it would be one of those "foolish" things users of your code can do to make your optimization dangerous.