-1

We have an emulated unsigned integer type, let's call it uint_t, which has operator unsigned int() implemented.

When we use a variable of this type as an array index:

#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("%s\n", myArray[i]);
    }

    for (unsigned int i = 0; i < sizeof(myArray)/sizeof(myArray[0]); i++)
    {
        printf("%s\n", myArray[i]);
    }

    return 0;
}

and compile it in Visual C++ 2015 x86 with all warnings enabled, we get this warning:

warning C4365: 'argument': conversion from 'unsigned int' to 'int', signed/unsigned mismatch

According to the C++ language spec (from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf)

8.2.1 Subscripting

1 A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall be a glvalue of type “array of T” or a prvalue of type “pointer to T” and the other shall be a prvalue of unscoped enumeration or integral type. The result is of type “T”. The type “T” shall be a completely-defined object type.66 The expression E1[E2] is identical (by definition) to *((E1)+(E2))...

Why does Visual C++ not accept a class with a single cast operator to unsigned int as an "integral type"?

Note: it is happy with the line using a real unsigned int, but not with the class with the unsigned int operator.

Note also: this only happens in x86 (32-bit) builds, not x64.

Ian Brockbank
  • 497
  • 5
  • 14
  • 3
    Whatever `myArray` is, it's not a standard `c` array or `std::array`. The type used to represent array indexes depends on the container you are accessing. In this case, `myArray`'s type expects `int`. If this is a problem, fix `myArray`. – François Andrieux Jun 19 '18 at 14:31
  • 2
    I don't see how the last question you ask (*"Why does Visual C++ not accept a class with a single cast operator to unsigned int as "integral type"?"*) has anything to do with the rest of the question title. – François Andrieux Jun 19 '18 at 14:33
  • 1
    What type is `myArray`? – Slava Jun 19 '18 at 14:47
  • 1
    I vote to close your question as it is useless without [mcve] – Slava Jun 19 '18 at 14:56
  • Okay, I've updated to make it clearer and give a full example which demonstrates the problem. Note: it _is_ a normal C array - I've used an array of const char * in the example. The only strange thing is using a class which implements unsigned int() instead of a native C++ integer. It also only happens in x86 builds (where there isn't a signed integer for unsigned int to extend into). – Ian Brockbank Jun 20 '18 at 08:54
  • @Slava I've updated the question for more clarity. It contains a complete verifiable example. Please update your response accordingly. – Ian Brockbank Jun 21 '18 at 12:05
  • @FrançoisAndrieux is it a completely standard C array, as demonstrated in the updated example. I have updated the title to clarify what I am asking. Please update your response as well. Thanks – Ian Brockbank Jun 21 '18 at 12:07
  • I'm not entirely sure why the warning is happening. Note that `*(myArray + i)` also causes the warning to appear. I still think the question is off base though. This isn't about Visual Studio not recognizing your type as an integral type. If it didn't, the code wouldn't compile at all. Neither is the segment you quoted relevant. You may get a better response to this question by making it more focused on the actual problem, the unexplained extra conversion to `int` that occurs. This seems like it's a compiler bug to me. – François Andrieux Jun 21 '18 at 14:41

3 Answers3

0

Out of curiosity, I've tried that on my own:

#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;
}

Output:

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

This was tested on coliru.com: Live Demo on coliru.

As OP mentioned Visual C++, I did the same test in VS2013 (64 bit) and got the exact equal output.

The warning 4365 didn't appear. (I switched to /Wall and got tons of warnings about std headers which I've never seen before.)

Please, note, that I get warning 4365 with the following change:

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

  operator unsigned int () const { return value; }
};

and this is caused by operator unsigned int () which now implicitly casts int value to unsigned int – a reasonable warning. However, the program prints again the same output (as expected).


As suggested by OP, I compiled the upper sample again in VS2013 but this time for 32 bit.

Now, I get the warning 4365 for the array subscript a[i].

There is no complaint about the vector subscript v[i].

I'm not sure whether it is worth to consider this warning for the array subscript too seriously. I mean it's a warning but no error.

I don't think that warnings shall be ignored in general (They shouldn't!) but in this specific case...


Btw. I digged a bit to deeper concerning std::vector::operator[]:

The const and non-const operator[] are declared with argument size_type as expected. I tried to fiddle out where size_type is actually defined but I got lost in the templates with templates... I expect that it is finally a typedef of std::size_t (the unsigned integer type of the result of the sizeof operator). So, of course, no warning 4365.

The operator[] for C arrays and raw pointers is hard to check as it's built-in the compiler. May be, you ask in MSDN regarding this...

May be, it's worth to check this in VS2017 before posting a bug report to MS. ;-)

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • Did you try this with an x86 build? I get the same warning with this code if I compile it for x86. – Ian Brockbank Jun 20 '18 at 08:52
  • @IanBrockbank Nope, in x64. (Usually, I note this but in this answer I forgot - too bad.) In x86, I can reproduce. Added some notes about this... – Scheff's Cat Jun 20 '18 at 12:20
  • @IanBrockbank Of course, I think it should work. I googled a bit concerning signedness vs. unsignedness of array/vector indices. On one hand, an array/vector index should never become negative. On the other hand, every good C/C++ book teachs you that `e1[e2]` is effectively nothing else than `*((e1) + (e2))`. So, regarding the latter, there is nothing wrong with adding a signed integral (e.g. `ptrdiff_t`) to a pointer. There are lots of discussion about this. So, what did I learn? I'm uncertain. I will just ignore this as before until I find a definitive recommendation how it is done right... – Scheff's Cat Jun 21 '18 at 16:05
0

Consensus seems to be that this should work and hence is a bug in the Visual C++ compiler for x86. I have reported it to Microsoft at https://developercommunity.visualstudio.com/content/problem/322701/cant-use-an-emulated-uint-type-as-an-array-subscri.html

I still see it with Visual Studio 2017 as well as Visual Studio 2015.

It builds perfectly with gcc.

Ian Brockbank
  • 497
  • 5
  • 14
0

The warning is generally useless, but the compiler is allowed to emit useless warnings about absolutely anything, as long as it still accepts valid programs.

The uselessness of the warning is an indication of a compiler bug.

Note there's no inconsistency between 32-bit and 64-bit versions, which can be seen by replacing unsigned int with size_t. With this change, both 32-bit and 64-bit versions will emit the same kind of useless warning.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243