使用表达式模板时,根据lvalue/rvalue提供类的不同实现

pep*_*ppe 20 c++ expression-templates auto c++11

问题

假设我们实现了一个string表示字符串的类.然后我们想要添加一个operator+连接两个strings的,并决定通过表达式模板实现它,以避免在执行时进行多次分配str1 + str2 + ... + strN.

运算符将如下所示:

stringbuilder<string, string> operator+(const string &a, const string &b)
Run Code Online (Sandbox Code Playgroud)

stringbuilder是一个模板类,它反过来重载operator+并具有隐式string转换运算符.几乎是标准的教科书练习:

template<class T, class U> class stringbuilder;

template<> class stringbuilder<string, string> {
    stringbuilder(const string &a, const string &b) : a(a), b(b) {};
    const string &a;
    const string &b;
    operator string() const;
    // ...
}

// recursive case similar,
// building a stringbuilder<stringbuilder<...>, string>
Run Code Online (Sandbox Code Playgroud)

只要有人这样做,上述实现就完美无缺

string result = str1 + str2 + ... + strN;
Run Code Online (Sandbox Code Playgroud)

但是,它有一个微妙的错误.将结果分配给正确类型的变量将使该变量保持对组成表达式的所有字符串的引用.这意味着,例如,更改其中一个字符串将更改结果:

void print(string);
string str1 = "foo";
string str2 = "bar";
right_type result = str1 + str2;
str1 = "fie";
print(result); 
Run Code Online (Sandbox Code Playgroud)

这将打印fiebar,因为str1引用存储在表达式模板中.它变得更糟:

string f();
right_type result = str1 + f();
print(result); // kaboom
Run Code Online (Sandbox Code Playgroud)

现在,表达式模板将包含对已销毁值的引用,直接使程序崩溃.

那是right_type什么?它当然stringbuilder<stringbuilder<...>, string>是表达模板魔术为我们生成的类型.

现在为什么会使用这样的隐藏类型?事实上,人们并没有明确地使用它 - 但是C++ 11的自动确实如此!

auto result = str1 + str2 + ... + strN; // guess what's going on here?
Run Code Online (Sandbox Code Playgroud)

这个问题

底线是:似乎这种实现表达式模板的方式(通过存储廉价引用而不是复制值或使用共享指针)一旦尝试存储表达式模板本身就会被破坏.

因此,我非常喜欢检测我是在构建rvalue还是左值,并提供表达式模板的不同实现,具体取决于是否构建了rvalue(保持引用)或构建了左值(制作副本) ).

是否有一个已建立的设计模式来处理这种情况?

我在研究期间唯一能够弄清楚的是那些

  1. 可以根据this是左值或右值来重载成员函数,即

    class C {
        void f() &; 
        void f() &&; // called on temporaries
    }
    
    Run Code Online (Sandbox Code Playgroud)

    但是,似乎我也不能在构造函数上做到这一点.

  2. 在C++中一个不能真正做``型重载"",即,提供相同类型的多个实施方式中,根据其种类将如何被使用(作为左值或右值创建的实例).

Cas*_*eri 13

我在评论中开始这个,但它有点大.那么,让我们回答一下(即使它真的没有回答你的问题).

这是一个已知的问题auto.例如,Herb Sutter 在这里已经讨论了它,Motti Lanzkron 在这里有更详细的讨论.

正如他们所说,委员会中有讨论要加入operator autoC++来解决这个问题.这个想法将取代(或除此之外)提供

operator string() const;
Run Code Online (Sandbox Code Playgroud)

如你所说,人们可以提供

string operator auto() const;
Run Code Online (Sandbox Code Playgroud)

用于类型推导上下文.在这种情况下,

auto result = str1 + str2 + ... + strN;
Run Code Online (Sandbox Code Playgroud)

不会推断出类型result是"正确的类型",而是类型,string因为这是operator auto()返回的.

AFAICT在C++ 14中不会发生这种情况.C++ 17 pehaps ......

  • @peppe如果有人制作了你的`stringbuilder`类型的明确副本,他们应该被允许在脚下射击,不是吗?:) (6认同)