#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;
}
출력이 -5
and 가 될 것으로 예상 5
했지만 출력은 -5
and -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 스타일 버전 의 함수에 대한 약한 기호의 교체 가능성에 달려 있습니다.
우리는 단순히 세계적으로 대체 할 경우주의 int
로 double
는 출력 것이다 – 위의 프로그램에서, (GCC 아래) 코드는 “예상대로”작동합니다 -5 5
. 이것은 C 표준 라이브러리에 abs(double)
기능 이 없기 때문에 발생 합니다. 우리 자신을 선언함으로써 abs(double)
우리는 아무것도 대체하지 않습니다.
그러나로 전환 한 후 경우 int
에 double
우리는 또한 전환 abs
에 fabs
, 원래의 이상한 행동은 한창 (출력에 다시 나타납니다 -5 -5
).
이것은 위의 설명과 일치합니다.
답변
코드로 인해 정의되지 않은 동작이 발생합니다.
C ++ 17 [extern.names] / 4 :
외부 연결로 선언 된 C 표준 라이브러리의 각 함수 서명은 extern “C”및 extern “C ++”연결이있는 함수 서명으로 사용하거나 전역 네임 스페이스에서 네임 스페이스 범위의 이름으로 사용하기 위해 구현에 예약되어 있습니다.
따라서 표준 C 라이브러리 함수와 동일한 프로토 타입으로 함수를 만들 수 없습니다. int abs(int);
. 실제로 포함하는 헤더 또는 해당 헤더가 C 라이브러리 이름을 전역 네임 스페이스에 넣는 지 여부에 관계없이.
그러나 abs
다른 매개 변수 유형을 제공하면 오버로드가 허용됩니다 .