UFO ET IT

클래스 내부의 실제 정적 상수에 대한 enum 대 constexpr

ufoet 2020. 11. 18. 21:51
반응형

클래스 내부의 실제 정적 상수에 대한 enum 대 constexpr


내 의도를 말하면서 시작하겠습니다. 예전 (C ++) 시절에는 다음과 같은 코드가있었습니다.

class C
{
public:
  enum {SOME_VALUE=27};
};

그런 다음 SOME_VALUE코드 전체에서 컴파일 시간 상수로 사용할 수 있으며 컴파일러가 볼 수있는 곳에 C::SOME_VALUE리터럴 27을 삽입합니다.

이제는 해당 코드를 다음과 같이 변경하는 것이 더 적합 해 보입니다.

class C
{
public:
  static constexpr int SOME_VALUE=27;
};

이것은 훨씬 깨끗해 보이고 SOME_VALUE잘 정의 된 유형을 제공 하며 C ++ 11에서 선호되는 접근 방식 인 것 같습니다. (적어도 나에게는 예상치 못한) 문제는 이것이 SOME_VALUE외부 만들어야하는 시나리오를 유발 한다는 것입니다. 즉, 어떤 cpp 파일 어딘가에 다음을 추가해야합니다.

constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage

이 문제를 일으키는 SOME_VALUE경우는 C ++ 표준 라이브러리 코드에서 자주 발생하는 const 참조 가 사용될 때인 것 같습니다 (이 질문의 맨 아래에있는 예제 참조). 그런데 gcc 4.7.2를 컴파일러로 사용하고 있습니다.

이 딜레마로 인해 SOME_VALUE일부 정적 constexpr 멤버 변수가 아닌 일부에 대한 정의를 cpp 파일에 추가하지 않으려면 열거 형 (예 : 구식)으로 다시 정의해야합니다. 컴파일러에게 이것이 컴파일 시간 상수 로만 처리되어야 하고 외부 링크가있는 객체가 아니라는 constexpr int SOME_VALUE=27것을 의미 하는 방법이 없습니까? 그것과 함께 사용되는 const 참조가 보이면 임시를 만드십시오. 해당 주소가 사용 된 것을 확인하면 필요한 경우 컴파일 시간 오류를 생성합니다. 이는 컴파일 시간 상수이고 그 이상은 아니기 때문입니다.SOME_VALUE

다음은 SOME_VALUEcpp 파일에 대한 정의를 추가해야하는 무해한 샘플 코드입니다 (다시 한 번 gcc 4.7.2로 테스트 됨).

#include <vector>

class C
{
public:
  static constexpr int SOME_VALUE=5;
};

int main()
{
  std::vector<int> iv;

  iv.push_back(C::SOME_VALUE); // Will cause an undefined reference error
                               // at link time, because the compiler isn't smart
                               // enough to treat C::SOME_VALUE as the literal 5
                               // even though it's obvious at compile time
}

파일 범위의 코드에 다음 줄을 추가하면 오류가 해결됩니다.

constexpr int C::SOME_VALUE;

기록을 위해 static constexpr버전은 C ++ 17에서 예상 한대로 작동합니다. N4618 부록 D.1 [depr.static_constexpr]에서 :

D.1 static constexpr데이터 멤버의 재 선언 [depr.static_constexpr]

이전 C ++ 국제 표준과의 호환성을 위해 constexpr이니셜 라이저없이 정적 데이터 멤버를 클래스 외부에서 중복해서 다시 선언 할 수 있습니다. 이 사용법은 더 이상 사용되지 않습니다. [ 예 :

struct A {
 static constexpr int n = 5; // definition (declaration in C++ 2014)
};

constexpr int A::n; // redundant declaration (definition in C++ 2014)

최종 예 ]

이를 허용하는 관련 표준 텍스트는 N4618 9.2.3 [class.static.data] / 3입니다 .

[...] 인라인 정적 데이터 멤버는 클래스 정의에서 정의 될 수 있으며 중괄호 또는 같음 이니셜 라이저를 지정할 수 있습니다 . 멤버가 constexpr지정자 와 함께 선언 된 경우 이니셜 라이저없이 네임 스페이스 범위에서 다시 선언 될 수 있습니다 (이 사용은 더 이상 사용되지 않습니다. D.1 참조). [...]

이것은 constexpr동일한 것, 인라인 정적 데이터 멤버 의 비 버전 을 도입 한 동일한 기계와 함께 제공됩니다 .

struct A {
 static inline int n = 5; // definition (illegal in C++ 2014)
}; 

inline int A::n; // illegal

여기에는 세 가지 옵션이 있습니다.

  1. 클래스가 템플릿이면 헤더 자체에 정적 멤버의 정의를 넣으십시오. 컴파일러는 여러 번역 단위에서 하나의 정의로만 식별해야합니다 ([basic.def.odr] / 5 참조).

  2. 클래스가 템플릿이 아닌 경우 소스 파일에 쉽게 넣을 수 있습니다.

  3. 또는 constexpr 정적 멤버 함수 getSomeValue ()를 선언합니다.

    class C
    {
    public:
        static constexpr int getSomeValue() { return 27; }
    };
    

I'd go with enum class:

http://en.cppreference.com/w/cpp/language/enum

http://www.stroustrup.com/C++11FAQ.html#enum

From the first link:

enum class Color { RED, GREEN=20, BLUE};
Color r = Color::BLUE;
switch(r) {
    case Color::RED : std::cout << "red\n"; break;
    case Color::GREEN : std::cout << "green\n"; break;
    case Color::BLUE : std::cout << "blue\n"; break;
}
// int n = r; // error: no scoped enum to int conversion
int n = static_cast<int>(r); // OK, n = 21

Nowadays, the preferred way is:

enum class : int C { SOME_VALUE = 5 };

From the C++ standard N3797 S3.5/2-3

A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope:

— When a name has external linkage , the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.

— When a name has internal linkage , the entity it denotes can be referred to by names from other scopes in the same translation unit.

— When a name has no linkage , the entity it denotes cannot be referred to by names from other scopes.

A name having namespace scope (3.3.6) has internal linkage if it is the name of

— a variable, function or function template that is explicitly declared static; or,

— a non-volatile variable that is explicitly declared const or constexpr and neither explicitly declared extern nor previously declared to have external linkage; or

— a data member of an anonymous union.

My reading is that in the following code:

public:
  static constexpr int SOME_VALUE=5;
  constexpr int SOME_VALUE=5;
};
static constexpr int SOME_VALUE=5;
constexpr int SOME_VALUE=5;

All 4 instances of SOME_VALUE have internal linkage. They should link with a reference to SOME_VALUE in the same translation unit and not be visible elsewhere.

Obviously the first one is a declaration and not a definition. It needs a definition within the same translation unit. If GCC says so and MSVC does not, then MSVC is wrong.

For the purposes of replacing an enum, number 2 should work fine. It still has internal linkage without the static keyword.

[Edited in response to comment]


you can do this

class C
{
public:
  static const int SOME_VALUE=5;
};

int main()
{
  std::vector<int> iv;
  iv.push_back(C::SOME_VALUE); 
}

This is not even C++11, just C++98

참고URL : https://stackoverflow.com/questions/22867654/enum-vs-constexpr-for-actual-static-constants-inside-classes

반응형