C++ 使用临时对象复制所需的构造函数

C++ 使用临时对象复制所需的构造函数,c++,copy-constructor,C++,Copy Constructor,以下代码仅在副本构造函数可用时才起作用 当我添加print语句(通过std::cout)并使copy构造函数可用时,它不会被使用(我假设会发生一些编译器技巧来删除不必要的副本) 但在两个输出中,运算符来自 绑定类类型的右值时 对于引用,复制构造函数 类的属性必须是可访问的。对于 实例中,考虑以下代码: 一开始这可能会令人惊讶 视觉,尤其是最流行的视觉 编译器无法正确实现 这条规则() 这将在C++1x中修复。本标准的适用部分为§8.5.3/5,其中包括引用的初始化,以及§3.10/6,其中说明什

以下代码仅在副本构造函数可用时才起作用

当我添加print语句(通过
std::cout
)并使copy构造函数可用时,它不会被使用(我假设会发生一些编译器技巧来删除不必要的副本)

但在两个输出中,运算符来自

绑定类类型的右值时 对于引用,复制构造函数 类的属性必须是可访问的。对于 实例中,考虑以下代码:

一开始这可能会令人惊讶 视觉,尤其是最流行的视觉 编译器无法正确实现 这条规则()


这将在C++1x中修复。

本标准的适用部分为§8.5.3/5,其中包括引用的初始化,以及§3.10/6,其中说明什么是右值,什么是左值(在C++中并不总是明显)

在本例中,您的初始化表达式是:“N(1)”,因此您使用函数表示法显式创建一个对象。根据3.10/6,该表达式为右值

然后我们必须按顺序浏览8.5.3/5中的规则,并使用第一个适用的规则。第一种可能是表达式表示左值,或者可以隐式转换为左值。您的表达式是一个右值,隐式转换为左值需要一个转换函数来返回一个引用,在本例中该引用似乎不存在,因此这似乎不适用

下一条规则说引用必须是常量T(这里就是这种情况)。在这种情况下,表达式是类类型的右值,并且引用与引用兼容(即,引用指向同一类或类的基)。这意味着在第151页底部(179的C++ 2003 PDF)的子弹似乎适用。在这种情况下,编译器可以直接将引用绑定到表示右值的对象,或者创建右值的临时副本并绑定到该临时副本

然而,无论哪种方式,该标准都明确要求:“无论复制是否实际完成,将用于制作复制的构造函数都应可调用。”

因此,我认为gcc给出错误消息是正确的,而其他人接受代码在技术上是错误的。我将您的代码简化为以下内容:

class N {
    public:
        N(int)  {}
    private:
        N(N const&);
};

void plop(N const& data) { }

int main() {
    plop(N(1));
}
当使用“-A”(严格错误模式)调用时,Comeau会给出以下错误消息:

"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was
          eliminated, is inaccessible
      plop(N(1));
           ^
同样,当使用“/Za”(其“符合ANSI标准”模式)调用时,VC++9给出:

plop.cpp
plop.cpp(12) : error C2248: 'N::N' : cannot access private member declared in class 'N'
        plop.cpp(6) : see declaration of 'N::N'
        plop.cpp(2) : see declaration of 'N'
        while checking that elided copy-constructor 'N::N(const N &)' is callable
        plop.cpp(6) : see declaration of 'N::N'
        when converting from 'N' to 'const N &'

我的猜测是,大多数其他编译器的功能大致相同。由于它们优化了对复制构造函数的调用,因此通常不要求它存在或可访问。当您要求他们尽可能准确地遵守标准时,他们会给出错误消息,因为即使他们不使用,这在技术上也是必需的。

哪个编译器?这是一个很好的编译VC9@Captain:不太好。两者都是有效的。我更喜欢上面使用的形式。
N const&
完全有效。甚至有一个很好的理由认为它更好。再加上Naveen的评论-VC6,Digital Mars和Comeau对你的例子也没有问题。只有GCC 3.4.5(我没有安装更新的版本)抱怨复制。我不明白为什么应该这样做。这听起来很像我最近遇到的一个问题:我可以看到语句“foo(makeA());”需要一个副本构造函数因为它是从函数makeA()复制回结果的结果(即使编译器删除了实际副本,它也应该在那里)。但是“foo(A())”语句不应该需要一个副本(除了要求它的标准之外),我希望第391版就是为了解决这个问题而设计的。这里真的有一个副本“plop(N(1));”?如果有,我不认为它是因为编译器可以将引用直接绑定到表示右值的对象。因此,不需要对话的内容或部分内容,也不需要复制。不幸的是,下一段(迫使符合条件的编译器产生错误)伤害了我。@Martin:我很难想象一个编译器会真正复制,即使它的优化程度最低(当然这几乎是不可能的)。最重要的是,大多数(即明智的)编译器通常不强制执行,而用C++ 0x,需求就消失了。
class N {
    public:
        N(int)  {}
    private:
        N(N const&);
};

void plop(N const& data) { }

int main() {
    plop(N(1));
}
"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was
          eliminated, is inaccessible
      plop(N(1));
           ^
plop.cpp
plop.cpp(12) : error C2248: 'N::N' : cannot access private member declared in class 'N'
        plop.cpp(6) : see declaration of 'N::N'
        plop.cpp(2) : see declaration of 'N'
        while checking that elided copy-constructor 'N::N(const N &)' is callable
        plop.cpp(6) : see declaration of 'N::N'
        when converting from 'N' to 'const N &'