Почему значение указателя на член всегда одинаково для разных членов одной и той же структуры?

У меня есть следующий код:

#include <iostream>
#include <string>

using namespace std;

struct foo_s {
    string a;
    string b;
    string c;
};

void print_field(foo_s* foo, string foo_s::* field) {
    cout << "field: " << field << " - " << foo->*field << endl;
}

int main() {
   foo_s my_foo = {
       "a",
       "b",
       "c",
   };

   print_field(&my_foo, &foo_s::a);
   print_field(&my_foo, &foo_s::b);
   print_field(&my_foo, &foo_s::c);

   return 0;
}

Его вывод:

field: 1 - a                                                                                                                                                                                                              
field: 1 - b                                                                                                                                                                                                              
field: 1 - c  

У меня возникли проблемы с пониманием специфики того, что происходит в функции print_field(). А именно:

  • Какой тип field? Я предполагаю, что это pointer-to-string-foo_s-member
  • Почему значение field всегда одно и то же (1 в этом случае), но foo->*field дает разные результаты?

В основном, я озадачен # 2. Я предположил, что поле будет "смещением" с начала структуры, а foo->*field было бы концептуально эквивалентно чему-то вроде

char* ptr = static_cast<char*>(foo);
ptrdiff_t offset = somehow_get_the_byte_offset_from_pointer_to_member(field);
ptr = ptr[offset];
string result = *static_cast<string*>(ptr);

но это кажется отсутствующим, поскольку значение field не зависит от вызовов. Что мне не хватает? Как именно эта конкретная операция описывается стандартом?

Ответ 1

Нет никакой перегрузки для << для форматирования значения указателя-члена, поэтому вы не получите ничего особенно полезного, если попытаетесь. Существует перегрузка для bool, а указатели элементов конвертируются в bool, так что это происходит здесь. Указатель не является нулевым, поэтому он преобразуется в true, который по умолчанию форматируется как 1.

Чтобы продемонстрировать, что вы можете сначала попробовать потоковое вещание boolalpha; то вы должны увидеть true, а не 1.

Ответ 2

  • Тип field - это, как вы говорите, указатель на элемент foo_s типа std::string.

  • Значение во всех этих случаях 1 равно 1, потому что указатели на член конвертируются в bool, поэтому при выводе их вы получаете 1, потому что они не равны нулю.