선언이 std 네임 스페이스에 영향을 미칠 수 있습니까? { int a =

#include <iostream>
#include <cmath>

/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
    return a > 0? -a : a;
}

int main() {
    int a = abs(-5);
    int b = std::abs(-5);
    std::cout<< a << std::endl << b << std::endl;
    return 0;
}

출력이 -5and 가 될 것으로 예상 5했지만 출력은 -5and -5입니다.

이 사건이 왜 일어날 지 궁금합니다.

사용과 관련이 std있습니까?



답변

언어 사양을 사용하면 전역 네임 스페이스 <cmath>에서 표준 함수를 선언 (및 정의) 한 다음 using-declarations를 통해 네임 스페이스로 가져와 구현을 구현할 수 있습니다 . 이 접근법이 사용되는지 여부는 지정되지 않았습니다.std

20.5.1.2 헤더
4 […] 그러나 C ++ 표준 라이브러리에서 선언 (C에서 매크로로 정의 된 이름 제외)은 네임 스페이스의 네임 스페이스 범위 (6.3.6) 내에 있습니다 std. 이러한 이름 (21 ~ 33 절 및 부록 D에 추가 된 오버로드 포함)이 먼저 전역 네임 스페이스 범위 내에서 선언 된 다음 std명시 적 using-declarations (10.3.3)에 의해 네임 스페이스 에 삽입되는지 여부는 지정되지 않습니다 .

분명히이 접근법을 따르기로 결정한 구현 중 하나 (예 : GCC)를 다루고 있습니다. 즉, 구현은을 제공 ::abs하지만 std::abs단순히를 “참조”합니다 ::abs.

이 경우에 남아있는 한 가지 질문은 표준에 추가 ::abs하여 자신의을 선언 할 수 있었던 ::abs이유, 즉 다중 정의 오류가없는 이유입니다. 이는 일부 구현 (예 : GCC)에서 제공하는 다른 기능으로 인해 발생할 수 있습니다. 표준 기능을 소위 약한 기호 로 선언합니다. 하여 사용자가 자신의 정의로 “대체”할 수 있도록합니다.

이 두 가지 요소가 함께 관찰 된 효과를 생성합니다. 약한 기호 ::abs를 대체하면 std::abs. 이것이 언어 표준에 얼마나 잘 부합하는지는 다른 이야기입니다 … 어쨌든이 행동에 의존하지 마십시오. 언어에 의해 보장되지 않습니다.

GCC에서이 동작은 다음과 같은 최소한의 예제로 재현 할 수 있습니다. 하나의 소스 파일

#include <iostream>

void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }

다른 소스 파일

#include <iostream>

void foo();
namespace N { using ::foo; }

void foo() { std::cout << "Goodbye!" << std::endl; }

int main()
{
  foo();
  N::foo();
}

이 경우 두 번째 소스 파일에서 ::foo( "Goodbye!") 의 새 정의가 의 동작에도 영향을 미치는 것을 관찰 할 수 있습니다 N::foo. 두 호출 모두 "Goodbye!". 그리고 ::foo두 번째 소스 파일에서 의 정의를 제거하면 두 호출 모두의 “원래”정의 ::foo및 출력으로 전달 "Hello!"됩니다.


위의 20.5.1.2/4에서 부여한 권한은 <cmath>. 구현은 단순히 C-style을 포함한 <math.h>다음 함수를 다시 선언하고 std일부 C ++ 관련 추가 및 조정을 추가 할 수 있습니다. 위의 설명이 문제의 내부 메커니즘을 적절하게 설명하는 경우 주요 부분은 C 스타일 버전 의 함수에 대한 약한 기호의 교체 가능성에 달려 있습니다.

우리는 단순히 세계적으로 대체 할 경우주의 intdouble는 출력 것이다 – 위의 프로그램에서, (GCC 아래) 코드는 “예상대로”작동합니다 -5 5. 이것은 C 표준 라이브러리에 abs(double)기능 이 없기 때문에 발생 합니다. 우리 자신을 선언함으로써 abs(double)우리는 아무것도 대체하지 않습니다.

그러나로 전환 한 후 경우 intdouble우리는 또한 전환 absfabs, 원래의 이상한 행동은 한창 (출력에 다시 나타납니다 -5 -5).

이것은 위의 설명과 일치합니다.


답변

코드로 인해 정의되지 않은 동작이 발생합니다.

C ++ 17 [extern.names] / 4 :

외부 연결로 선언 된 C 표준 라이브러리의 각 함수 서명은 extern “C”및 extern “C ++”연결이있는 함수 서명으로 사용하거나 전역 네임 스페이스에서 네임 스페이스 범위의 이름으로 사용하기 위해 구현에 예약되어 있습니다.

따라서 표준 C 라이브러리 함수와 동일한 프로토 타입으로 함수를 만들 수 없습니다. int abs(int); . 실제로 포함하는 헤더 또는 해당 헤더가 C 라이브러리 이름을 전역 네임 스페이스에 넣는 지 여부에 관계없이.

그러나 abs다른 매개 변수 유형을 제공하면 오버로드가 허용됩니다 .