避免使用表达式模板#define

Cli*_*ton 1 c++ expression-templates c++11

使用以下代码时,不显示"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
}
Run Code Online (Sandbox Code Playgroud)

澄清:

请注意,我相信Boost uBLAS存储引用,这就是为什么我不想存储副本.如果您建议我按值存储,请解释为什么Boost uBLAS出错并且按值存储不会影响性能.

Luc*_*ton 5

通过引用存储的表达式模板通常是为了提高性能,但需要注意的是它们只能用作临时表

取自Boost.Proto 的文档(可用于创建表达式模板):

注意精明的读者会注意到上面定义的对象y将保留对临时int的悬空引用.在Proto解决的各种高性能应用程序中,通常在任何临时对象超出范围之前构建和评估表达式树,因此这种悬空参考情况通常不会出现,但它肯定是需要注意的事情. .Proto提供了深层复制表达式树的实用程序,因此它们可以作为值类型传递,而不用担心悬空引用.

在您的初始示例中,这意味着您应该:

std::cout << C(std::string("hello2")).s << std::endl;
Run Code Online (Sandbox Code Playgroud)

这样C临时永远不会超过std::string临时.或者,您可以s像其他人指出的那样成为非参考成员.

既然你提到了C++ 11,将来我希望表达式树按值存储,使用移动语义来避免昂贵的复制和包装,如std :: reference_wrapper,仍然可以通过引用存储.这样可以很好地发挥作用auto.

您的代码可能是C++ 11版本:

class C
{
public:
    explicit
    C(std::string const& s_): s { s_ } {}

    explicit
    C(std::string&& s_): s { std::move(s_) } {}

    std::string const&
    get() const& // notice lvalue *this
    { return s; }

    std::string
    get() && // notice rvalue *this
    { return std::move(s); }

private:
    std::string s; // not const to enable moving
};
Run Code Online (Sandbox Code Playgroud)

这意味着代码C("hello").get()只会分配一次内存,但仍然可以很好地使用

std::string clvalue("hello");
auto c = C(clvalue);
std::cout << c.get() << '\n'; // no problem here
Run Code Online (Sandbox Code Playgroud)