问题描述
我在使用C ++类型特征时经历了一些奇怪的行为,并将我的问题缩小到了这个古怪的小问题,我会给出大量的解释,因为我不想留下任何可能导致误解的东西。
假设你有这样的程序:
#include< iostream>
#include< cstdint>
模板< typename T>
bool is_int64(){return false; }
模板<>
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;
返回0;
$ b $ p $在这两种32位编译GCC(以及32位和64位编译)这个程序的输出是:
pre $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 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 int 和 int64_t相同类型,所以在逻辑上, int64_t , long int 和 long long int 将是等效类型 - 使用这些类型时生成的程序集是相同的。看看 stdint.h 告诉我为什么:
#if __WORDSIZE == 64
typedef long int int64_t;
#else
__extension__
typedef long long int int64_t;
#endif
在64位编译中, int64_t 是 long int ,而不是 long long int (显然)。
这种情况的修复非常简单:
#if defined(__ GNUC__)& amp ;&安培; (__WORDSIZE == 64)
模板<>
bool is_int64< long long int>(){return true; }
#endif
但是这是可怕的骇人听闻,并且不能很好地扩展(实际的功能物质, uint64_t 等)。 所以我的问题是:有没有办法告诉编译器: long long int 也是 int64_t ,就像 long int 是?
我最初的想法是,这是不可能的,因为C / C ++类型定义的工作方式。没有办法指定编译器的基本数据类型的类型等价性,因为这是编译器的工作(并且允许它可能会破坏很多东西)并且 typedef 只有一种方式。
我也不太在意在这里得到答案,因为这是一个超级骗子的边缘案例,我不怀疑任何人会永远关心什么时候这些例子没有可怕的设计(这是否意味着这应该是社区维基?)。
追加:我使用部分模板特化的原因,而不是像下面这样简单的例子:
void go int64_t){}
int main()
{
long long int x = 2;
go(x);
返回0;
}
是上述示例仍然会编译,因为 long long int 可隐式转换为 int64_t 。
追加:到目前为止的唯一答案是假设我想知道类型是否为64位。我不想误导人们认为我很在意这一点,可能应该提供更多的例子来说明这个问题在哪里出现。
template< typename T>
struct some_type_trait:boost :: false_type {};
模板<>
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 Concepts中很常见):
template< typename T>
void same_type(T,T){}
void foo()
{
long int x;
long long int y;
same_type(x,y);
}
该示例无法编译,因为C ++(正确)认为类型是不同。 g ++将无法编译,如下所示:没有匹配的函数调用 same_type(long int&; long long int&)。
我想强调一点,我明白为什么发生这种情况,但我正在寻找一种解决方法,它不会强制我在所有地方重复代码。
你不需要去64位看到这样的东西。在普通的32位平台上考虑 int32_t 。它可能是 typedef 'ed为 int 或者作为 long ,但显然一次只有两个中的一个。 int 和 long 当然是不同的类型。
不难发现,在32位系统上没有使 int == int32_t == long 的解决方法。出于同样的原因,在64位系统上无法使 long == int64_t == long long 。
<如果可以的话,对于重载 foo(int), foo(long)$ c $的代码,可能的后果是相当痛苦的c>和 foo(long long) - 突然他们对同一个重载有两个定义?!
正确的解决方案是你的模板代码通常不应该依赖于一个精确的类型,而是依赖于那种类型的属性。对于特定情况,整个 same_type 逻辑仍然可以:
long foo(长x);
std :: tr1 :: disable_if(same_type(int64_t,long),int64_t):: type foo(int64_t);
即超载 foo(int64_t) 与 foo(long) 相同
$ b 编辑]
使用C ++ 11,我们现在有一个标准的写法:
long foo(long X);
std :: enable_if<!std :: is_same< int64_t,long> :: value,int64_t> :: type foo(int64_t);
I experienced some odd behavior while using C++ type traits and have narrowed my problem down to this quirky little problem for which I will give a ton of explanation since I do not want to leave anything open for misinterpretation.
Say you have a program like so:
#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; }
In both 32-bit compile with GCC (and with 32- and 64-bit MSVC), the output of the program will be:
int: 0 int64_t: 1 long int: 0 long long int: 1
However, the program resulting from a 64-bit GCC compile will output:
int: 0 int64_t: 1 long int: 1 long long int: 0
This is curious, since long long int is a signed 64-bit integer and is, for all intents and purposes, identical to the long int and int64_t types, so logically, int64_t, long int and long long int would be equivalent types - the assembly generated when using these types is identical. One look at stdint.h tells me why:
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif
In a 64-bit compile, int64_t is long int, not a long long int (obviously).
The fix for this situation is pretty easy:
#if defined(__GNUC__) && (__WORDSIZE == 64) template <> bool is_int64<long long int>() { return true; } #endif
But this is horribly hackish and does not scale well (actual functions of substance, uint64_t, etc). So my question is: Is there a way to tell the compiler that a long long int is the also a int64_t, just like long int is?
My initial thoughts are that this is not possible, due to the way C/C++ type definitions work. There is not a way to specify type equivalence of the basic data types to the compiler, since that is the compiler's job (and allowing that could break a lot of things) and typedef only goes one way.
I'm also not too concerned with getting an answer here, since this is a super-duper edge case that I do not suspect anyone will ever care about when the examples are not horribly contrived (does that mean this should be community wiki?).
Append: The reason why I'm using partial template specialization instead of an easier example like:
void go(int64_t) { } int main() { long long int x = 2; go(x); return 0; }
is that said example will still compile, since long long int is implicitly convertible to an int64_t.
Append: The only answer so far assumes that I want to know if a type is 64-bits. I did not want to mislead people into thinking that I care about that and probably should have provided more examples of where this problem manifests itself.
template <typename T> struct some_type_trait : boost::false_type { }; template <> struct some_type_trait<int64_t> : boost::true_type { };
In this example, some_type_trait<long int> will be a boost::true_type, but some_type_trait<long long int> will not be. While this makes sense in C++'s idea of types, it is not desirable.
Another example is using a qualifier like same_type (which is pretty common to use in C++0x Concepts):
template <typename T> void same_type(T, T) { } void foo() { long int x; long long int y; same_type(x, y); }
That example fails to compile, since C++ (correctly) sees that the types are different. g++ will fail to compile with an error like: no matching function call same_type(long int&, long long int&).
I would like to stress that I understand why this is happening, but I am looking for a workaround that does not force me to repeat code all over the place.
You don't need to go to 64-bit to see something like this. Consider int32_t on common 32-bit platforms. It might be typedef'ed as int or as a long, but obviously only one of the two at a time. int and long are of course distinct types.
It's not hard to see that there is no workaround which makes int == int32_t == long on 32-bit systems. For the same reason, there's no way to make long == int64_t == long long on 64-bit systems.
If you could, the possible consequences would be rather painful for code that overloaded foo(int), foo(long) and foo(long long) - suddenly they'd have two definitions for the same overload?!
The correct solution is that your template code usually should not be relying on a precise type, but on the properties of that type. The whole same_type logic could still be OK for specific cases:
long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
I.e., the overload foo(int64_t) is not defined when it's exactly the same as foo(long).
[edit]With C++11, we now have a standard way to write this:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
这篇关于C ++:long long int与long int与int64_t的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!