메서드 체인의 C ++ 실행 순서 *ar =

이 프로그램의 출력 :

#include <iostream>
class c1
{
  public:
    c1& meth1(int* ar) {
      std::cout << "method 1" << std::endl;
      *ar = 1;
      return *this;
    }
    void meth2(int ar)
    {
      std::cout << "method 2:"<< ar << std::endl;
    }
};

int main()
{
  c1 c;
  int nu = 0;
  c.meth1(&nu).meth2(nu);
}

Is :

method 1
method 2:0

시작할 nu때 1 이 아닌 이유는 무엇 meth2()입니까?



답변

평가 순서가 지정되지 않았기 때문입니다.

당신은 전에 평가받는 것을보고 있습니다 nu.main0meth1 호출된다. 이것이 연결 문제입니다. 하지 않는 것이 좋습니다.

멋지고 간단하고 명확하고 읽기 쉽고 이해하기 쉬운 프로그램을 만드십시오.

int main()
{
  c1 c;
  int nu = 0;
  c.meth1(&nu);
  c.meth2(nu);
}


답변

평가 순서에 관한 표준 초안 의이 부분 이 적절 하다고 생각합니다 .

1.9 프로그램 실행

  1. 언급 된 경우를 제외하고 개별 연산자의 피연산자 및 개별 식의 하위 식에 대한 평가는 순서가 지정되지 않습니다. 연산자의 피연산자의 값 계산은 연산자 결과의 값 계산 전에 순서가 지정됩니다. 스칼라 객체에 대한 부작용이 동일한 스칼라 객체에 대한 다른 부작용 또는 동일한 스칼라 객체의 값을 사용하는 값 ​​계산과 관련하여 순서가 지정되지 않고 잠재적으로 동시 적이 지 않은 경우 동작이 정의되지 않습니다.

그리고 또한:

5.2.2 함수 호출

  1. [참고 : 접미사 식과 인수의 평가는 모두 서로에 대해 순서가 지정되지 않습니다. 인수 평가의 모든 부작용은 함수가 입력되기 전에 순서가 지정됩니다 — 끝 참고]

따라서 라인의 c.meth1(&nu).meth2(nu);경우 최종 호출에 대한 함수 호출 연산자의 관점에서 연산자에서 무슨 일이 발생하는지 고려 meth2하여 접미사 표현식과 인수로의 분석을 명확하게 볼 수 있습니다 nu.

operator()(c.meth1(&nu).meth2, nu);

최종 함수 호출 (즉, 후위 식 및 ) 에 대한 후위 식 및 인수평가는 위 의 함수 호출 규칙에 따라 서로에 대해 순서 가 지정 되지 않습니다 . 따라서 스칼라 객체에 대한 접미사 표현식 계산의 부작용 은 함수 호출 이전 의 인수 평가와 관련하여 순서가 지정되지 않습니다. 위 의 프로그램 실행 규칙에 따르면 이것은 정의되지 않은 동작입니다.c.meth1(&nu).meth2nuarnumeth2

즉, 컴파일러가 호출 후 호출에 대한 nu인수 를 평가할 필요가 없습니다. 즉, meth2호출 meth1에 영향을 meth1미치는 부작용이 없다고 가정 할 수 있습니다 .nu . 평가에 .

위에서 생성 된 어셈블리 코드는 main함수에 다음 시퀀스를 포함합니다 .

  1. 변하기 쉬운 nu 는 스택에 할당되고 0으로 초기화됩니다.
  2. 레지스터 (ebx 제 경우)는 다음 값의 사본을받습니다.nu
  3. 의 주소 nuc 매개 변수 레지스터에로드됩니다.
  4. meth1 불린다
  5. 반환 값 레지스터와 이전에 캐시 된 값nu에서ebx 레지스터 파라미터 레지스터에로드
  6. meth2 불린다

중요하게도 위의 5 단계에서 컴파일러는 nu2 단계 의 캐시 된 값을 에 대한 함수 호출에서 재사용 할 수 있습니다 meth2. 여기서는 작동중인 ‘정의되지 않은 동작’ nu호출에 의해 변경되었을 수 있는 가능성을 무시합니다 meth1.

참고 : 이 답변은 원래 형식에서 실질적으로 변경되었습니다. 마지막 함수 호출 전에 순서가 지정되지 않은 피연산자 계산의 부작용에 대한 초기 설명은 정확하지 않기 때문입니다. 문제는 피연산자 자체의 계산이 불확실하게 배열된다는 사실입니다.


답변

1998 년 C ++ 표준, 섹션 5, para 4

언급 된 경우를 제외하고 개별 연산자의 피연산자 및 개별 식의 하위 식에 대한 평가 순서와 부작용이 발생하는 순서는 지정되지 않습니다. 이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 스칼라 객체는 표현식 평가에 의해 최대 한 번 수정 된 저장된 값을 가져야합니다. 또한 이전 값은 저장할 값을 결정하기 위해서만 액세스해야합니다. 이 단락의 요구 사항은 전체 표현의 하위 표현의 허용 가능한 각 순서에 대해 충족되어야합니다. 그렇지 않으면 동작이 정의되지 않습니다.

(이 질문과 관련이없는 각주 # 53에 대한 참조는 생략했습니다.)

기본적으로 &nu호출하기 전에 평가해야 c1::meth1()하고, nu호출하기 전에 평가되어야한다 c1::meth2(). 그러나 nu이전에 평가해야하는 요구 사항은 없습니다 &nu(예 : nu먼저 평가 &nuc1::meth1()다음를 호출 한 다음 호출 하는 것이 허용됩니다. 이것이 컴파일러가 수행하는 작업 일 수 있음). 표현 *ar = 1c1::meth1()따라서 이전에 평가되는 것은 아닙니다 nu인은 main()에 전달하기 위해, 평가c1::meth2() .

이후의 C ++ 표준 (현재 내가 오늘 밤 사용하는 PC에는없는)은 본질적으로 동일한 절을 가지고 있습니다.


답변

컴파일 할 때 meth1 및 meth2 함수가 실제로 호출되기 전에 매개 변수가 전달되었다고 생각합니다. “c.meth1 (& nu) .meth2 (nu);”를 사용할 때 nu = 0 값이 meth2에 전달되었으므로 “nu”가 후자 변경 되더라도 상관 없습니다.

당신은 이것을 시도 할 수 있습니다 :

#include <iostream>
class c1
{
public:
    c1& meth1(int* ar) {
        std::cout << "method 1" << std::endl;
        *ar = 1;
        return *this;
    }
    void meth2(int* ar)
    {
        std::cout << "method 2:" << *ar << std::endl;
    }
};

int main()
{
    c1 c;
    int nu = 0;
    c.meth1(&nu).meth2(&nu);
    getchar();
}

그것은 당신이 원하는 답을 얻을 것입니다


답변