developer tip

정적 const int에 대한 정의되지 않은 참조

copycodes 2020. 10. 27. 08:20
반응형

정적 const int에 대한 정의되지 않은 참조


오늘 흥미로운 문제가 발생했습니다. 이 간단한 예를 고려하십시오.

template <typename T>
void foo(const T & a) { /* code */ }

// This would also fail
// void foo(const int & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

int main()
{
   Bar b;
   b.func();
}

컴파일 할 때 오류가 발생합니다.

Undefined reference to 'Bar::kConst'

자, 나는 이것이 static const int어디에도 정의되지 않았기 때문이라고 확신합니다. 이것은 내 이해에 따르면 컴파일러가 컴파일 타임에 대체를 할 수 있어야하고 정의가 필요하지 않기 때문입니다. 그러나 함수는 const int &매개 변수를 취하기 때문에 대체를하지 않고 대신 참조를 선호하는 것 같습니다. 다음과 같이 변경하여이 문제를 해결할 수 있습니다.

foo(static_cast<int>(kConst));

나는 이것이 컴파일러가 임시 int를 만들고 그에 대한 참조를 전달하도록 강요하고 있다고 믿습니다. 이것은 컴파일 타임에 성공적으로 수행 할 수 있습니다.

이것이 의도적 인 것인지 궁금하거나 gcc에서이 사건을 처리 할 수있을 것으로 기대하고 있습니까? 아니면 내가 어떤 이유로해서는 안되는 일인가?


의도적입니다. 9.4.2 / 4는 다음과 같이 말합니다.

정적 데이터 멤버가 const 정수 또는 const 열거 형인 경우 클래스 정의의 선언은 정수 상수 식 (5.19)이 될 상수 이니셜 라이저를 지정할 수 있습니다.이 경우 멤버는 정수 상수 식에 나타날 수 있습니다. 멤버는 프로그램에서 사용되는 경우 네임 스페이스 범위에서 정의됩니다.

const 참조로 정적 데이터 멤버를 전달할 때 3.2 / 2를 "사용"합니다.

정수 상수 표현식이 필요한 곳에 나타나거나 (5.11 참조), sizeof 연산자의 피연산자 (5.3.3)이거나, typeid 연산자의 피연산자이고 표현식이 다음의 lvalue를 지정하지 않는 경우 표현식은 잠재적으로 평가됩니다. 다형성 클래스 유형 (5.2.8). 개체 또는 오버로드되지 않은 함수는 이름이 잠재적으로 평가 된 식에 나타나는 경우 사용됩니다.

따라서 실제로 값으로 전달하거나 static_cast. 단지 GCC가 한 경우에 당신을 낚아 채게했지만 다른 경우에는 그렇지 않습니다.

[편집 : gcc는 C ++ 0x 초안의 규칙을 적용합니다. "이름이 잠재적으로 평가 된 표현식으로 나타나는 변수 또는 오버로드되지 않은 함수는 상수에 표시하기위한 요구 사항을 충족하는 객체가 아닌 한 odr 사용됩니다. 표현식 (5.19)과 lvalue에서 rvalue 로의 변환 (4.1)이 즉시 적용됩니다. " 정적 캐스트는 lvalue-rvalue 변환을 즉시 수행하므로 C ++ 0x에서는 "사용"되지 않습니다.]

const 참조의 실제 문제 foo는 인수의 주소를 가져 와서이를 글로벌에 저장된 다른 호출의 인수 주소와 비교할 수있는 권한 내에 있다는 것 입니다. 정적 데이터 멤버는 고유 한 개체이므로 foo(kConst)두 개의 다른 TU에서 호출하는 경우 전달 된 개체의 주소가 각 경우에 동일해야합니다. AFAIK GCC는 개체가 하나의 TU에 정의되어 있지 않으면이를 정렬 할 수 없습니다.

foo좋습니다. 이 경우 는 템플릿이므로 정의는 모든 TU에서 볼 수 있습니다. 따라서 아마도 컴파일러는 주소로 무엇이든 할 위험을 이론적으로 배제 할 수 있습니다. 그러나 일반적으로 존재하지 않는 객체에 대한 주소 또는 참조를 사용해서는 안됩니다 ;-)


클래스 선언 내부에 이니셜 라이저를 사용하여 정적 const 변수를 작성하는 경우 마치 작성한 것처럼

class Bar
{
      enum { kConst = 1 };
}

GCC는 동일한 방식으로 처리합니다. 즉, 주소가 없습니다.

올바른 코드는

class Bar
{
      static const int kConst;
}
const int Bar::kConst = 1;

이것은 정말 유효한 경우입니다. 특히 때문에 같은 STL에서 기능 할 수있는 표준 : 카운트 얻어 CONST T 및 세 번째 인수로한다.

링커가 이러한 기본 코드에 문제가있는 이유를 이해하려고 많은 시간을 보냈습니다.

오류 메시지

'Bar :: kConst'에 대한 정의되지 않은 참조

링커가 기호를 찾을 수 없음을 알려줍니다.

$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst

We can see from the 'U' that Bar::kConst is undefined. Hence, when the linker tries to do its job, it has to find the symbol. But you only declare kConst and don't define it.

The solution in C++ is also to define it as follows:

template <typename T>
void foo(const T & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

const int Bar::kConst;       // Definition <--FIX

int main()
{
   Bar b;
   b.func();
}

Then, you can see that the compiler will put the definition in the generated object file:

$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst

Now, you can see the 'R' saying that it is defined in the data section.


g++ version 4.3.4 accepts this code (see this link). But g++ version 4.4.0 rejects it.


You can also replace it by a constexpr member function:

class Bar
{
  static constexpr int kConst() { return 1; };
};

I think this artefact of C++ means that any time that Bar::kConst is referred to, its literal value is used instead.

This means that in practise there is no variable to make a reference point to.

You may have to do this:

void func()
{
  int k = kConst;
  foo(k);
}

Simple trick: use + before the kConst passed down the function. This will prevent the constant from being taken a reference from, and this way the code will not generate a linker request to the constant object, but it will go on with the compiler-time constant value instead.


I experienced the same problem as mentioned by Cloderic (static const in a ternary operator: r = s ? kConst1 : kConst2), but it only complained after turning off compiler optimization (-O0 instead of -Os). Happened on gcc-none-eabi 4.8.5.

참고URL : https://stackoverflow.com/questions/5391973/undefined-reference-to-static-const-int

반응형