2009-12-01 5 views
10

다음 코드는 복사 생성자가 사용 가능한 경우에만 작동합니다.복사 생성자 임시 개체와 함께 필요

프린트 문을 (std::cout을 통해) 추가하고 사용 가능한 복사 생성자를 만들 때 (필자는 불필요한 복사본을 제거하는 컴파일러 트릭이 있다고 가정합니다).

아래의 출력 operator <<과 아래의 함수 plop() (여기서 임시 개체를 만들 때)은 복사 생성자가 필요하지 않습니다. 누군가 const 참조 (또는 내가 잘못하고있는 일)로 모든 것을 전달할 때 언어가 필요로하는 이유를 설명 할 수 있습니까?

#include <iostream> 

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

std::ostream& operator<<(std::ostream& str,N const& data) 
{ 
    return str << "N\n"; 
} 

void plop(std::ostream& str,N const& data) 
{ 
    str << "N\n"; 
} 

int main() 
{ 
    std::cout << N(1);  // Needs copy constructor (line 25) 
    plop(std::cout,N(1)); // Needs copy constructor 

    N a(5); 
    std::cout << a; 
    plop(std::cout,a); 
} 

컴파일러 :

[Alpha:~/X] myork% g++ -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5646~6/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5646)

[Alpha:~/X] myork% g++ t.cpp
t.cpp: In function ‘int main()’:
t.cpp:10: error: ‘N::N(const N&)’ is private
t.cpp:25: error: within this context
t.cpp:10: error: ‘N::N(const N&)’ is private
t.cpp:26: error: within this context

이 진짜 코드의 단순화 된 버전입니다.
실제 코드에는 std :: auto_ptr이 포함 된 클래스가 있습니다. 클래스도

변경 :

class N 
{ 
    public: 
     N(int) {} 
    private: 
     std::auto_ptr<int> data; 
}; 
이것은 const를 참조 소요 복사 생성자는 (몇 가지 작업없이) 유효하지 않습니다 내가 복사 생성자가 그것 때문에 사용할 수 없음을 나타내는 오류가 발생하고 있음을 의미

오류는 다음과 같습니다

t.cpp:25: error: no matching function for call to ‘N::N(N)’

+3

컴파일러는 무엇입니까? 이것은 VC9에서 잘 컴파일됩니다. – Naveen

+0

N (N const &)은 N (const N &)이어야합니다. –

+6

@Captain : 실제로는 아닙니다. 둘 다 유효합니다. 나는 위에서 사용하는 형식을 선호합니다. –

답변

15

When binding an rvalue of class type to a reference, the copy constructor of the class must be accessible. For instance, consider the following code:

http://gcc.gnu.org/gcc-3.4/changes.html에서
class A 
{ 
public: 
    A(); 

private: 
    A(const A&); // private copy ctor 
}; 

A makeA(void); 
void foo(const A&); 

void bar(void) 
{ 
    foo(A());  // error, copy ctor is not accessible 
    foo(makeA()); // error, copy ctor is not accessible 

    A a1; 
    foo(a1);  // OK, a1 is a lvalue 
} 

This might be surprising at first sight, especially since most popular compilers do not correctly implement this rule (further details).

이 항목은 C++ 1x에서 Core Issue 391으로 고정됩니다.

+0

'foo (makeA());'문에 대한 복사 생성자가 필요하다는 것을 알 수 있습니다. 결과는 makeA() 함수에서 다시 복사 한 결과입니다 (컴파일러가 실제 사본을 제거하더라도 해당 사본이 있어야 함). 그러나 'foo (A())'문은 391 호가 해결하도록 설계된 사본 (표준 요구 사항은 별도)을 요구하지 않아야합니다. –

5

표준의 해당 부분은 참조의 초기화와 rvalue 및 lvalue (C++에서는 항상 명확하지는 않음)를 알려주는 §3.10/6을 다루는 §8.5.3/5입니다.

이 경우 초기화 표현식은 "N (1)"이므로 함수 표기법을 사용하여 명시 적으로 객체를 만듭니다. 3.10/6에 따르면, 그 표현은 rvalue입니다.

그런 다음 8.5.3/5 규칙을 순서대로 따라야하며 적용되는 첫 번째 규칙을 사용해야합니다. 첫 번째 가능성은 표현식이 lvalue를 나타내거나 암시 적으로 lvalue로 변환 될 수있는 경우입니다. 표현식은 rvalue이고 암시 적으로 lvalue로 변환하려면 참조가 반환되는 변환 함수가 필요합니다.이 경우에는 존재하지 않는 것처럼 보이므로 적용되지 않습니다.

다음 규칙에서는 참조가 const T 여야한다고 말합니다 (여기서는이 경우). 이 경우 표현식은 클래스 유형의 rvalue이며 참조와 참조 호환됩니다 (즉, 참조가 동일한 클래스 또는 클래스의 기본에 있음). 즉, 151 페이지 (C++ 2003 PDF의 179 페이지) 하단에있는 글 머리 기호가 적용되는 것입니다. 이 경우 컴파일러는 rvalue를 나타내는 객체에 직접 참조를 바인딩하거나 rvalue의 임시 복사본을 만들고 해당 임시 복사본에 바인딩 할 수 있습니다.

그러나 어느 쪽이든, 표준에는 명시 적으로 다음 사항이 명시되어 있습니다. "사본을 만드는 데 사용되는 생성자는 사본이 실제로 완료되었는지 여부에 관계없이 호출 가능해야합니다.. "이와 같이

, 나는 GCC는 오류 메시지를 제공하는 권리입니다 믿을 하고, 다른 사람이 코드를 받아 들일 기술적으로 잘못 난 당신의 코드를 단순화 다음에 비트 :

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

void plop(N const& data) { } 

int main() { 
    plop(N(1)); 
} 

(그것 "ANSI가 준수"모드) "/ ZA"로 호출 할 때, VC++의 9 제공, 마찬가지로

"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was 
      eliminated, is inaccessible 
     plop(N(1)); 
     ^

"--A"(엄격한 오류 모드)를 호출 할 때, 꼬모는 다음과 같은 오류 메시지를 제공합니다 :

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 &' 

대부분의 다른 컴파일러는 거의 같습니다. 그들은 복사 생성자에 대한 호출을 최적화하기 때문에 일반적으로 존재하지 않거나 액세스 가능할 것을 요구하지 않습니다. 가능한 한 정확하게 표준에 부합하도록 요청하면 오류 메시지가 나타납니다. 사용하지 않아도 기술적으로 필요하기 때문에 오류 메시지가 나타납니다.

+0

실제로 복사본이 여기에 'plop (N (1));'이 있습니까? 만약 컴파일러가 rvalue를 나타내는 객체에 직접적으로 참조를 바인딩 할 수 있기 때문에 나는 그것을 보지 못한다. 따라서 대화의 OR 부분은 필요하지 않으므로 복사 할 필요가 없습니다. 불행히도 다음 단락은 컴파일러가 오류를 발생하도록합니다. –

+0

@Martin : 최소한의 최적화 수준으로 복사를 할 수있는 컴파일러를 상상할 수는 없지만 (당연히 가능하지만). 실버 라이닝은 대부분의 (즉, 합리적인) 컴파일러가 보통 그것을 강제하지 않으며, C++ 0x를 사용하면 요구 사항이 사라질 것입니다. –