Programming

C ++ 객체를 자체 생성자에 전달하는 것이 합법적입니까?

procodes 2020. 8. 4. 20:02
반응형

C ++ 객체를 자체 생성자에 전달하는 것이 합법적입니까?


우연히 다음과 같은 효과가 있음을 발견했습니다.

#include <iostream>            
int main(int argc, char** argv)
{
  struct Foo {
    Foo(Foo& bar) {
      std::cout << &bar << std::endl;
    }
  };
  Foo foo(foo); // I can't believe this works...
  std::cout << &foo << std::endl; // but it does...
}

생성 된 객체의 주소를 자체 생성자로 전달합니다. 이것은 소스 수준에서 원형 정의처럼 보입니다. 표준을 통해 객체가 생성되기 전에 객체를 함수에 전달할 수 있습니까? 아니면 정의되지 않은 동작입니까?

모든 클래스 멤버 함수에 이미 클래스 인스턴스의 데이터에 대한 포인터가 암시 적 매개 변수로 있다고 가정하면 그다지 이상하지 않다고 생각합니다. 그리고 데이터 멤버의 레이아웃은 컴파일 타임에 고정됩니다.

이것이 유용한 지 또는 좋은 아이디어인지 묻지는 않습니다. 수업에 대해 더 많이 배우려고 노력하고 있습니다.


이것은 정의되지 않은 동작이 아닙니다. foo초기화되지 않았지만 표준에서 허용하는 방식으로 사용하고 있습니다. 공간이 객체에 할당 된 후 완전히 초기화되기 전에 제한된 방식으로 사용할 수 있습니다. 해당 변수에 대한 참조를 바인딩하고 주소를 사용할 수 있습니다.

이것은 결함 보고서 363 : 자기 자신으로부터 클래스의 초기화에 의해 다루어진다 :

그렇다면 UDT 자체 초기화의 의미는 무엇입니까? 예를 들어

 #include <stdio.h>

 struct A {
        A()           { printf("A::A() %p\n",            this);     }
        A(const A& a) { printf("A::A(const A&) %p %p\n", this, &a); }
        ~A()          { printf("A::~A() %p\n",           this);     }
 };

 int main()
 {
  A a=a;
 }

컴파일하고 인쇄 할 수 있습니다.

A::A(const A&) 0253FDD8 0253FDD8
A::~A() 0253FDD8

해상도는 다음과 같습니다.

3.8 [basic.life] 6 항은 여기에 언급 된 내용이 유효 함을 나타냅니다. 완전히 초기화되기 전에 클래스 객체의 주소를 사용할 수 있으며 참조가 직접 바인딩 될 수있는 한 참조 매개 변수에 인수로 전달할 수 있습니다. printfs에서 % p에 대해 포인터를 void *로 캐스팅하지 못한 경우를 제외하고이 예제는 표준을 준수합니다.

C ++ 14 표준 초안에서 3.8 [basic.life] 섹션의 전체 인용은 다음과 같습니다.

마찬가지로 객체의 수명이 시작되기 전이지만 객체가 차지하는 저장 공간이 할당 된 후 또는 객체의 수명이 종료 된 후 및 객체가 차지하는 저장 공간이 재사용되거나 해제되기 전에 해당하는 glvalue 원본 객체는 제한적인 방식으로 만 사용할 수 있습니다. 건설 중이거나 파괴중인 물체는 12.7을 참조하십시오. 그렇지 않으면 이러한 glvalue는 할당 된 저장소 (3.7.4.2)를 나타내며 해당 값에 의존하지 않는 glvalue의 속성을 사용하는 것이 잘 정의되어 있습니다. 다음과 같은 경우 프로그램에 정의되지 않은 동작이 있습니다.

  • lvalue-to-rvalue 변환 (4.1)이 이러한 glvalue에 적용됩니다.

  • glvalue는 비 정적 데이터 멤버에 액세스하거나 오브젝트의 비 정적 멤버 함수를 호출하는 데 사용됩니다.

  • glvalue가 가상 기본 클래스 (8.5.3)에 대한 참조에 바인딩되어 있거나

  • glvalue는 dynamic_cast (5.2.7)의 피연산자 또는 typeid의 피연산자로 사용됩니다.

foo위의 글 머리 기호에 정의 된대로 정의되지 않은 동작에 해당되는 작업은 수행하지 않습니다 .

Clang으로 이것을 시도하면 불길한 경고가 나타납니다 ( 살펴보십시오 ).

경고 : 변수 'foo'는 자체 초기화 내에서 사용될 때 초기화되지 않습니다 [-Wuninitialized]

초기화되지 않은 자동 변수에서 결정되지 않은 값을 생성하는 것은 정의되지 않은 동작 이므로 올바른 경고 입니다. 그러나이 경우 참조를 바인딩하고 생성자 내에서 변수의 주소를 가져 와서 결정되지 않은 값을 생성하지 않고 유효합니다. 한편, C ++ 11 표준 초안다음 자체 초기화 예제다음과 같습니다 .

int x = x ;

정의되지 않은 동작을 호출합니다.

진행중인 문제 453 : 참조는 "유효한"개체에만 바인딩 될 수 있지만 관련성이 있지만 여전히 열려 있습니다. 처음 제안 된 언어는 결함 보고서 363과 일치합니다.


The constructor is called at a point where memory is allocated for the object-to-be. At that point, no object exists at that location (or possibly an object with a trivial destructor). Furthermore, the this pointer refers to that memory and the memory is properly aligned.

Since it's allocated and aligned memory, we may refer to it using lvalue expressions of Foo type (i.e. Foo&). What we may not yet do is have an lvalue-to-rvalue conversion. That's only allowed after the constructor body is entered.

In this case, the code just tries to print &bar inside the constructor body. It would even be legal to print bar.member here. Since the constructor body has been entered, a Foo object exists and its members may be read.

This leaves us with one small detail, and that's name lookup. In Foo foo(foo), the first foo introduces the name in scope and the second foo therefore refers back to the just-declared name. That's why int x = x is invalid, but int x = sizeof(x) is valid.

참고URL : https://stackoverflow.com/questions/32608458/is-passing-a-c-object-into-its-own-constructor-legal

반응형