В чем такое отличие x86 и x64 в VC++ 2019

Навеяно другим вопросом, пытаясь ответить на который, обнаружил такое непонятное мне поведение VC++ 2019.

Код:

#include <iostream>

using namespace std;

class String
{
public:
    String(char* str):p(str){};
    const char& operator[](size_t i) const { return p[i]; }
    char& operator[](size_t i)             { return p[i]; }
    operator const char* () const          { return p; }
    operator char* ()                      { return p; }
private:
    char* p;
};

int main() {
    char s[] = "123456";
    String name = s;
    std::cout << name[0] << std::endl;
    name[1] = '5';
    std::cout << name << std::endl;
}

На всякие возможные проблемы с памятью и иже с ними не смотрите, это просто урезанный до минимума код.

Так вот, при компиляции x64 строкой cl /EHsc main.cpp все проходит на ура.

А вот той же командой для x86 получаем

main.cpp  
main.cpp(20): error C2666: String::operator []: для перегрузок (4) есть подобные преобразования  
main.cpp(10): note: может быть "char &String::operator [](size_t)"  
main.cpp(9): note: или       "const char &String::operator [](size_t) const"  
main.cpp(20): note: или       "встроенный оператор C++[(const char *, int)"  
main.cpp(20): note: или       "встроенный оператор C++[(char *, int)"  
main.cpp(20): note: при попытке сопоставить список аргументов "(String, int)"  
main.cpp(21): error C2666: String::operator []: для перегрузок (4) есть подобные преобразования
main.cpp(10): note: может быть "char &String::operator [](size_t)"
main.cpp(9): note: или       "const char &String::operator [](size_t) const"
main.cpp(21): note: или       "встроенный оператор C++[(const char *, int)"
main.cpp(21): note: или       "встроенный оператор C++[(char *, int)"
main.cpp(21): note: при попытке сопоставить список аргументов "(String, int)"

Кто-то может пояснить, почему такое отличие при компиляции под разные платформы?...

P.S. G++: https://ideone.com/TbDfC2


Ответы (1 шт):

Автор решения: Pak Uula

g++ ведёт себя аналогично: g++ -m32 -c some.c++ -pedantic

some.c++: In function ‘int main()’:
some.c++:20:22: error: ambiguous overload for ‘operator[]’ (operand types are ‘String’ and ‘int’)
     std::cout << name[0] << std::endl;
                      ^
some.c++:20:22: note: candidate: operator[](const char*, int) <built-in>
some.c++:20:22: note: candidate: operator[](char*, int) <built-in>
some.c++:9:17: note: candidate: const char& String::operator[](size_t) const
     const char& operator[](size_t i) const { return p[i]; }
                 ^~~~~~~~
some.c++:10:11: note: candidate: char& String::operator[](size_t)
     char& operator[](size_t i)             { return p[i]; }
           ^~~~~~~~
some.c++:21:9: error: ambiguous overload for ‘operator[]’ (operand types are ‘String’ and ‘int’)
     name[1] = '5';
         ^
some.c++:21:9: note: candidate: operator[](const char*, int) <built-in>
some.c++:21:9: note: candidate: operator[](char*, int) <built-in>
some.c++:9:17: note: candidate: const char& String::operator[](size_t) const
     const char& operator[](size_t i) const { return p[i]; }
                 ^~~~~~~~
some.c++:10:11: note: candidate: char& String::operator[](size_t)
     char& operator[](size_t i)             { return p[i]; }
           ^~~~~~~~```

32-х битный компилятор находит четыре варианта:

  1. Оставить тип String, преобразовать int в size_t: char& String::operator[](size_t)
  2. Оставить тип String, преобразовать int в size_t: const char& String::operator[](size_t) const
  3. Преобразовать тип String в char*, оставить тип int: [char *, int]
  4. Преобразовать тип String в const char*, оставить тип int: [const char *, int]

Во всех четырех вариантах один тип преобразуется, второй нет. Поэтому компилятор не может выбрать лучший кандидат.

В 64-х битном случае ситуация иная: встроенный оператор [] имеет сигнатуру operator[](char*, long int)

Так как литерал 0 имеет тип int, то опции 3 и 4 из списка выше превращаются в

  1. Преобразовать тип String в char*, преобразовать тип int в long int: [char *, long int]

  2. Преобразовать тип String в const char*, преобразовать тип int в long int: [const char *, long int]

Так как добавляется Promotion, который ухудшает кандидата, то компилятор отбрасывает эти кандидаты. Поэтому в 64-х битном случае компилируется без ошибок.

Чтобы запретить компилятору рассматривать встроенные операторы индексирования, явным образом сделайте литералы беззнаковыми: 0u и 5u. Тогда будет компилироваться и 64, и 32 бита.

PS. Тип встроенного оператора []

ISO C++ 2020, глава 12.7

16 For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form ... T & operator[](T *, std::ptrdiff_t);

То, что 64 битный g++ для ptrdiff_t использует long int, я установил экспериментально.

→ Ссылка