为什么这里需要复制构造函数?

HC4*_*ica 5 c++ ternary-operator copy-constructor

请考虑以下代码:

struct S
{
    S() {}
    void f();
private:
    S(const S&);
};

int main()
{
    bool some_condition;
    S my_other_S;
    (some_condition ? S() : my_other_S).f();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

gcc无法编译,说:

test.cpp: In function 'int main()':
test.cpp:6:5: error: 'S::S(const S&)' is private
test.cpp:13:29: error: within this context
Run Code Online (Sandbox Code Playgroud)

我不明白为什么复制构造应该在那一行上进行 - 目的是简单地调用f()默认构造的S实例,或者它my_other_S,即它应该等同于:

if (some_condition)
    S().f();
else
    my_other_S.f();
Run Code Online (Sandbox Code Playgroud)

第一种情况有什么不同,为什么需要复制构造函数?

编辑:那么,有没有办法在表达式上下文中表达"在预先存在的对象上执行此操作"?

Pup*_*ppy 9

结果?:是rvalue,一个新对象,如果其中一个参数是rvalue.要创建此rvalue,编译器必须复制结果.

if (some_condition)
    S().f(); // Compiler knows that it's rvalue
else
    my_other_S.f(); // Compiler knows that it's lvalue
Run Code Online (Sandbox Code Playgroud)

这是出于同样的原因,你不能这样做

struct B { private: B(const B&); };
struct C { C(B&); C(const B&); };
int main() {
    B b;
    C c(some_condition ? b : B());
}
Run Code Online (Sandbox Code Playgroud)

我改变了我的榜样,因为旧的有点吮吸.你可以清楚地看到这里没有办法编译这个表达式,因为编译器无法知道要调用的构造函数.当然,在这种情况下,编译器可以强制两个参数const B&,但由于某些不太相关的原因,它不会.

编辑:不,没有,因为没有办法编译该表达式,因为有关它的重要数据(rvalue或lvalue)在运行时会有所不同.编译器尝试通过复制构造转换为rvalue来为您解决此问题,但它不能因为它无法复制,因此无法编译.


Ben*_*igt 8

来自[expr.cond](n3242草案的措辞):

否则,如果第二个和第三个操作数具有不同的类型并且具有(可能是cv限定的)类类型,或者两者都是相同值类别的glvalues和除cv-qualification之外的相同类型,则尝试转换每个那些操作数与另一种操作数的关系.确定E1类型的操作数表达式是否T1可以转换为匹配E2类型的操作数表达式的过程T2定义如下:

  • 如果E2是左值:E1可以转换为匹配,E2如果E1可以隐式转换(第4节)到类型"左值引用T2",受到约束,在转换中引用必须直接绑定(8.5.3)到左值.
  • 如果E2是xvalue:E1可以转换为匹配,E2如果E1可以隐式转换为"rvalue reference to T2" 类型,则受到引用必须直接绑定的约束.
  • 如果E2是rvalue,或者上述转换都不能完成,并且至少有一个操作数具有(可能是cv-qualified)类类型:

    • 如果E1E2具有类类型,和底层类类型是相同的或一个是基类的其他的: E1可以转换为匹配E2如果类的T2是相同的类型,或基类,类的T1,并cv资格证书T2与cv资格证书相同,或者是cv资格证书T1.如果施加了转换,E1改变为类型的prvalue T2通过复制初始化临时类型的T2E1和使用该临时作为转换操作数.

此规则提到了复制初始化,但由于两个操作数具有相同的类型,因此不适用

如果第二个和第三个操作数是相同值类别的glvalues并且具有相同的类型,则结果是该类型和值类别,如果第二个或第三个操作数是位字段,则它是位字段,或者如果两者都是位字段.

此规则不适用,因为它S()是一个rvalue并且my_other_S是一个左值.

否则,结果是prvalue.如果第二个和第三个操作数不具有相同的类型,并且具有(可能是cv限定的)类类型,则使用重载决策来确定要应用于操作数的转换(如果有)(13.3.1.2,13.6) .如果重载决策失败,则程序格式错误.否则,应用如此确定的转换,并使用转换的操作数代替本节其余部分的原始操作数.Lvalue-to-rvalue(4.1),array-to-pointer(4.2)和函数到指针(4.3)标准转换在第二个和第三个操作数上执行.完成转换后,以下其中一项应成立:

  • 第二和第三个操作数具有相同的类型; 结果是那种类型.如果操作数具有类类型,则结果是结果类型的prvalue临时值,它根据第一个操作数的值从第二个操作数或第三个操作数进行复制初始化.

应用此规则,结果是复制初始化(强调我的).