C ++에서 long long int 대 long int 대 int64_t


87

나는 C ++ 유형 특성을 사용하는 동안 이상한 행동을 경험했고 내 문제를이 기발한 작은 문제로 좁혔습니다.

다음과 같은 프로그램이 있다고 가정 해 보겠습니다.

#include <iostream>
#include <cstdint>

template <typename T>
bool is_int64() { return false; }

template <>
bool is_int64<int64_t>() { return true; }

int main()
{
 std::cout << "int:\t" << is_int64<int>() << std::endl;
 std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl;
 std::cout << "long int:\t" << is_int64<long int>() << std::endl;
 std::cout << "long long int:\t" << is_int64<long long int>() << std::endl;

 return 0;
}

GCC (및 32 비트 및 64 비트 MSVC)를 사용한 32 비트 컴파일에서 프로그램의 출력은 다음과 같습니다.

int:           0
int64_t:       1
long int:      0
long long int: 1

그러나 64 비트 GCC 컴파일 결과 프로그램은 다음을 출력합니다.

int:           0
int64_t:       1
long int:      1
long long int: 0

이후 이것은 호기심 long long int부호 64 비트 정수와 동일한 모든 의도와 목적을 위해, 인 long intint64_t종류이므로, 논리적 int64_t, long intlong long int이러한 유형의 동일 사용시 어셈블리 - 생성 등가 유형 것이다. 한 번 보면 stdint.h그 이유를 알 수 있습니다.

# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

64 비트 컴파일에서 int64_tis long int, not a long long int(분명히).

이 상황에 대한 수정은 매우 쉽습니다.

#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif

그러나 이것은 끔찍하게 해킹되고 잘 확장되지 않습니다 (실체의 실제 기능 uint64_t등). 그래서 제 질문은 : 컴파일러에게 할 수있는 방법이있는가 long long int(가)도있다 int64_t처럼 long int입니까?


내 초기 생각은 C / C ++ 유형 정의가 작동하는 방식으로 인해 이것이 가능하지 않다는 것입니다. 기본 데이터 유형의 유형 동등성을 컴파일러에 지정하는 방법은 없습니다. 이것이 컴파일러의 작업이고 (많은 것을 허용 할 수 있음) typedef한 방향으로 만 진행 되기 때문입니다 .

나는 또한 예제가 끔찍하게 고안되지 않았을 때 아무도 신경 쓰지 않을 것이라고 생각하지 않는 초 고급 사례이기 때문에 여기에서 대답을 얻는 데 너무 관심이 없습니다 (이것이 커뮤니티 위키이어야 함을 의미합니까?). .


Append : 다음과 같은 더 쉬운 예제 대신 부분 템플릿 전문화를 사용하는 이유 :

void go(int64_t) { }

int main()
{
    long long int x = 2;
    go(x);
    return 0;
}

long long int암시 적으로 int64_t.


Append : 지금까지 유일한 대답은 유형이 64 비트인지 알고 싶다고 가정합니다. 나는 사람들이 내가 그것에 관심이 있고 아마도이 문제가 어디에서 나타나는지에 대한 더 많은 예를 제공 했어야한다고 오해하고 싶지 않았습니다.

template <typename T>
struct some_type_trait : boost::false_type { };

template <>
struct some_type_trait<int64_t> : boost::true_type { };

이 예에서, some_type_trait<long int>될 것입니다 boost::true_type,하지만 some_type_trait<long long int>하지 않습니다. 이것은 C ++의 유형 개념에서 의미가 있지만 바람직하지 않습니다.

또 다른 예는 다음과 같은 한정자를 사용하는 것입니다 same_type(C ++ 0x 개념에서 사용하는 것이 매우 일반적입니다).

template <typename T>
void same_type(T, T) { }

void foo()
{
    long int x;
    long long int y;
    same_type(x, y);
}

이 예제는 컴파일에 실패합니다. C ++는 (올바르게) 유형이 다르다는 것을 인식하기 때문입니다. g ++는 다음과 같은 오류와 함께 컴파일에 실패합니다 : no matching function call same_type(long int&, long long int&).

이런 일이 발생 하는지 이해하고 있음을 강조하고 싶지만 여기 저기 코드를 반복하도록 강요하지 않는 해결 방법을 찾고 있습니다.


호기심으로 샘플 프로그램이 sizeof각 유형 에 대해 동일한 결과를 제공 합니까? 아마도 컴파일러는 크기를 long long int다르게 취급하고있을 것입니다 .
Blair Holloway

C ++ 0x를 사용하여 컴파일 했습니까? C ++ 03에는이 없습니다 <cstdint>. 따라서 "이것은 확장입니다"라고 말해야한다는 사실이이를 속이는 것입니다.
GManNickG 2010

예, --std=c++0x. 그리고 예, sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8.
Travis Gockel

1
아무도 아직 언급하지 않지만 경우에 그것을 간과했다 : longlong long(그들은 같은 크기 및 표현을 경우에도) 구별 유형이다. int64_t는 항상 다른 기존 유형의 별칭입니다 (이름에도 불구하고 typedef새 유형을 생성하지 않고 이미 존재하는 유형에 별칭을 제공함)
MM

3
답변 / 댓글에서 한 가지 중요한 진술이 누락되어이 문제가 저를 때렸을 때 도움이 되었습니다. 안정적으로 전문화 된 템플릿에 고정 크기 유형을 사용하지 마십시오. 항상 기본 유형을 사용하고 가능한 모든 경우를 다루십시오 (고정 크기 유형을 사용하여 해당 템플릿을 인스턴스화하는 경우에도). 가능한 모든 경우 는 다음을 의미합니다.으로 인스턴스화해야하는 경우 and int16_t로 전문화 하면 처리됩니다. (그리고 모험심이 있다면)shortintsigned char
Irfy

답변:


49

이와 같은 것을보기 위해 64 비트로 이동할 필요는 없습니다. int32_t일반적인 32 비트 플랫폼을 고려하십시오 . 또는로 typedef'ed' 일 수 있지만 분명히 한 번에 둘 중 하나만 있습니다. 그리고 물론 구별 유형의이다.intlongintlong

int == int32_t == long32 비트 시스템에서 만드는 해결 방법이 없다는 것을 확인하는 것은 어렵지 않습니다 . 같은 이유로 long == int64_t == long long64 비트 시스템에서는 만들 방법이 없습니다 .

당신이 할 수 있다면, 가능한 결과에 과부하가 그 코드 오히려 고통스러운 것 foo(int), foo(long)그리고 foo(long long)- 갑자기 그들은 같은 과부하에 대한 두 가지 정의를 것입니다!

올바른 해결책은 템플릿 코드가 일반적으로 정확한 유형이 아니라 해당 유형의 속성에 의존해야한다는 것입니다. same_type특정 경우에는 전체 논리가 여전히 괜찮을 수 있습니다.

long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);

즉은, 과부하는 foo(int64_t)이 때 정의되지 않은 정확히 같은 foo(long).

C ++ 11을 사용하면 이제이를 작성하는 표준 방법이 생겼습니다.

long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);

또는 C ++ 20

long foo(long x);
int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);

1
슬픈 소식은, 예를 들어 64 비트 MSVC19 (2017)에 인 sizeof() longint동일하지만 std::is_same<long, int>::value돌아갑니다 false. OSX HighSierra의 AppleClang 9.1에서도 마찬가지입니다.
Ax3l

3
@ Ax3l : 이상하지 않습니다. ISO C 90 이후 거의 모든 컴파일러에는 이러한 쌍이 하나 이상 있습니다.
MSalters

사실, 그것들은 별개의 유형입니다.
Ax3l

6

유형이 int64_t와 동일한 유형인지 알고 싶거나 64 비트인지 알고 싶습니까? 제안한 솔루션을 기반으로 후자에 대해 묻는 것 같습니다. 이 경우 다음과 같이 할 것입니다.

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64

1
a return와 세미콜론 이 누락되지 않았 습니까?
casablanca

1
그래도 sizeof이를 위해 사용해야합니다 .
Ben Voigt

5
long long int와 long int는 같은 크기인지 여부에 관계없이 동일한 유형이 아닙니다. 동작은 잘못된 것이 아닙니다. 그것은 단지 C ++입니다.
Logan Capaldo

5
명목 입력의 제한이 아닙니다. 무의미한 명목 입력 의 한계입니다 . 예전에는 사실상 표준은 short= 16 비트, long= 32 비트, int= 기본 크기였습니다. 64 비트의 요즘,에서 intlong하지 않는 평균 이상 아무것도.
dan04 2010

1
@ dan04 : 그들은 그 어느 때보 다 더 많거나 덜 의미가 없습니다. short적어도 16 비트 것은 int적어도 16 비트이며, long단락 <= INT <= 길이 (조잡 표기법 이하)과 함께, 적어도 32 비트이다. 당신이 언급 한 "오래된 날들"은 존재하지 않았습니다. 언어에 의해 부과 된 제한에는 항상 변화가있었습니다. "모든 세상은 x86"오류는 "모든 세상은 VAX 오류입니다."
Keith Thompson

1

그래서 내 질문은 : long int와 마찬가지로 long long int도 int64_t라고 컴파일러에게 알리는 방법이 있습니까?

이것은 좋은 질문이나 문제이지만 대답은 아니오라고 생각합니다.

또한 a long intlong long int.


# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

나는 이것이 libc라고 믿습니다. 더 깊이 들어가고 싶은 것 같아요.

GCC (및 32 비트 및 64 비트 MSVC)를 사용한 32 비트 컴파일에서 프로그램의 출력은 다음과 같습니다.

int:           0
int64_t:       1
long int:      0
long long int: 1

32 비트 Linux는 ILP32 데이터 모델을 사용합니다. 정수, long 및 포인터는 32 비트입니다. 64 비트 유형은 long long.

Microsoft는 데이터 유형 범위에 범위를 문서화합니다 . 는 말 long long과 동일합니다 __int64.

그러나 64 비트 GCC 컴파일 결과 프로그램은 다음을 출력합니다.

int:           0
int64_t:       1
long int:      1
long long int: 0

64 비트 Linux는 LP64데이터 모델을 사용합니다 . Long은 64 비트이고 long long64 비트입니다. 32 비트와 마찬가지로 Microsoft는 데이터 유형 범위에서 범위를 문서화 하고 long long은 여전히 __int64.

ILP64모든 것이 64 비트 인 데이터 모델 이 있습니다 . word32유형에 대한 정의를 얻으려면 추가 작업을 수행해야 합니다. 또한 64-Bit Programming Models : Why LP64인가?


그러나 이것은 끔찍한 해킹이며 잘 확장되지 않습니다 (실체의 실제 기능, uint64_t 등) ...

예, 더 좋아집니다. GCC는 64 비트 유형을 취해야하는 선언을 혼합하고 일치 시키므로 특정 데이터 모델을 따르더라도 문제가 발생하기 쉽습니다. 예를 들어, 다음은 컴파일 오류를 발생시키고 다음을 사용하도록 지시합니다 -fpermissive.

#if __LP64__
typedef unsigned long word64;
#else
typedef unsigned long long word64;
#endif

// intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864)
// extern int _rdrand64_step(unsigned __int64 *random_val);

// Try it:
word64 val;
int res = rdrand64_step(&val);

결과는 다음과 같습니다.

error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'

따라서 무시 LP64하고 다음으로 변경하십시오.

typedef unsigned long long word64;

그런 다음 LP64NEON 을 정의 하고 사용 하는 64 비트 ARM IoT 가젯으로 이동합니다 .

error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.