copy elision:在return语句中使用三元表达式时调用未调用的构造函数?

时间:2012-08-11 12:26:32

标签: c++ c++11

考虑以下示例:

#include <cstdio>

class object
{
public:
    object()
    {
        printf("constructor\n");
    }

    object(const object &)
    {
        printf("copy constructor\n");
    }

    object(object &&)
    {
        printf("move constructor\n");
    }
};

static object create_object()
{
    object a;
    object b;

    volatile int i = 1;

// With #if 0, object's copy constructor is called; otherwise, its move constructor.
#if 0
    if (i)
        return b; // moves because of the copy elision rules
    else
        return a; // moves because of the copy elision rules
#else
    // Seems equivalent to the above, but behaves differently.
    return i ? b : a; // copies (with g++ 4.7)
#endif
}

int main()
{
    auto data(create_object());

    return 0;
}

考虑一下C ++ 11 Working Draft,n3337.pdf,12.8 [class.copy],第32点:

  

当满足或将满足复制操作的省略标准时,除了源对象是函数参数之外,并且要复制的对象由左值指定,重载决策要选择首先执行副本的构造函数,就像对象是由右值指定的一样。如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。 [注意:无论是否发生复制省略,都必须执行此两阶段重载决策。如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数。 - 后注]

因此,如果我们在示例中使用#if 1,则在返回对象时首先尝试移动构造函数,然后再复制构造函数。由于我们有一个移动构造函数,因此使用它来代替复制构造函数。

然而,在return中的最后一个create_object()语句中,我们发现没有使用移动构造函数。这是不是违反了语言规则?语言是否要求在最后return语句中使用移动构造函数?

1 个答案:

答案 0 :(得分:10)

条件运算符的规范非常复杂,这很可怕。但我相信你的编译器的行为是正确的。见5.16 [expr.cond] / p4:

  

如果第二个和第三个操作数是相同值的glvalues   类别和具有相同的类型,结果是该类型和值   类别......

另见12.8 [class.copy],p31,b1,其中描述了何时允许复制省略:

  

在具有类返回类型的函数中的return语句中,何时   表达式是非易失性自动对象的名称(其他   比函数或catch子句参数)具有相同的cv-   非限定类型作为函数返回类型,复制/移动操作   可以省略...

表达式不是自动对象的名称,而是条件表达式(并且该条件表达式是左值)。因此,此处不允许复制省略,并且没有其他任何内容表明左值表达式可以假装为重载解析的右值。