为什么我不能使用在 Visual C++ 32 位中实现运算符无符号 int() 作为数组索引的类?

Why can't I use a class which implements operator unsigned int() as an array index in Visual C++ 32-bit?

本文关键字:int 无符号 数组 索引 运算符 实现 不能 Visual C++ 为什么      更新时间:2023-10-16

我们有一个模拟的无符号整数类型,我们称之为uint_t,它operator unsigned int()实现。

当我们使用这种类型的变量作为数组索引时:

#pragma warning(disable: 4514)  // unreferenced inline function removed
#pragma warning(disable: 4710)  // function not inlined
#pragma warning(disable: 4711)  // function selected for inline expansion
#include <cstdio>
class my_uint_t
{
public:
my_uint_t(unsigned int val) { m_val = val; }
operator unsigned int() const { return m_val; }
unsigned int operator ++(int) { return m_val++; }
private:
unsigned int m_val;
};
int main(int, const char **)
{
const char *myArray[] = { "1", "2", "3", "4", "5" };
for (my_uint_t i = 0; i < sizeof(myArray)/sizeof(myArray[0]); i++)
{
printf("%sn", myArray[i]);
}
for (unsigned int i = 0; i < sizeof(myArray)/sizeof(myArray[0]); i++)
{
printf("%sn", myArray[i]);
}
return 0;
}

并在启用所有警告的情况下在Visual C++ 2015 x86中编译它,我们得到以下警告:

警告 C4365:"参数":从"无符号 int"转换为"int",有符号/无符号不匹配

根据C++语言规范(从 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf 开始(

8.2.1 下标

1 后缀表达式后跟方括号中的表达式是后缀表达式。其中之一 表达式应为"T 数组"类型的 glvalue 或 键入"指向 T 的指针",另一个应为"无作用域"的 prvalue 枚举或整型类型。结果的类型为"T"。类型"T" 应为完全定义的对象类型。66 表达式 E1[E2] 为 与 *((E1(+(E2((相同(根据定义(...

为什么 Visual C++不接受具有单个强制转换运算符的类作为"整型"unsigned int

注意:它对使用真正的无符号 int 的行感到满意,但对带有无符号 int 运算符的类不满意。

另请注意:这仅在 x86(32 位(版本中发生,而不是 x64。

出于好奇,我自己尝试过:

#include <iostream>
#include <vector>
struct UInt {
unsigned value;
UInt(unsigned value = 0): value(value) { }
UInt(const UInt&) = default;
operator unsigned int () const { return value; }
};
int main()
{
UInt i(2);
int a[] = { 0, 1, 2, 3, 4, 5 };
std::cout << "a[UInt(2)]: " << a[i] << 'n';
std::vector<int> v(std::begin(a), std::end(a));
std::cout << "v[UInt(2)]: " << v[i] << 'n';
return 0;
}

输出:

a[UInt(2)]: 2
v[UInt(2)]: 2

这在 coliru.com:coliru上的现场演示中进行了测试。

正如OP提到的Visual C++,我在VS2013(64位(中做了同样的测试,得到了完全相同的输出。

警告 4365 未出现。(我切换到/Wall,并收到了大量关于std标题的警告,这是我以前从未见过的。

请注意,我收到警告 4365 并显示以下更改:

struct UInt {
int value;
UInt(int value = 0): value(value) { }
UInt(const UInt&) = default;
operator unsigned int () const { return value; }
};

这是由operator unsigned int ()造成的,它现在隐含地将int value投向unsigned int- 一个合理的警告。但是,程序会再次打印相同的输出(如预期的那样(。


按照OP的建议,我在VS2013中再次编译了上面的样本,但这次是32位

现在,我收到数组下标a[i]的警告 4365。

没有关于矢量下标v[i]的抱怨。

我不确定是否值得过于认真地考虑数组下标的警告。我的意思是这是一个警告,但没有错误。

我不认为警告一般应该被忽略(他们不应该!(但在这种特定情况下......


顺便说一句。我深入挖掘了一下std::vector::operator[]

const和非常量operator[]按预期使用参数size_type声明。我试图摆弄size_type实际定义的位置,但我迷失在带有模板的模板中......我希望它最终是std::size_t的 typedef(sizeof 运算符结果的无符号整数类型(。所以,当然,没有警告4365。

C 数组和原始指针的operator[]很难检查,因为它内置在编译器中。也许,你在MSDN中问这个...

也许,在向MS发布错误报告之前,值得在VS2017中检查这一点。

共识似乎是这应该有效,因此是 x86 的 Visual C++ 编译器中的一个错误。 我已经向Microsoft报告了 https://developercommunity.visualstudio.com/content/problem/322701/cant-use-an-emulated-uint-type-as-an-array-subscri.html

我仍然在Visual Studio 2017和Visual Studio 2015中看到它。

它与 gcc 完美构建。

警告通常是无用的,但编译器可以对绝对任何事情发出无用的警告,只要它仍然接受有效的程序。

警告的无用性表示编译器错误。

请注意,32 位和 64 位版本之间没有不一致,这可以通过将unsigned int替换为size_t来查看。通过此更改,32 位和 64 位版本都将发出相同类型的无用警告。