Почему std:: cout конвертирует изменчивые указатели в bool?

Если вы пытаетесь отключить указатель на volatile тип, даже volatile char указатель, где вы обычно ожидаете, что cout будет печатать строку, вместо этого вы просто получите "1" (если указатель не является нулевым, я думаю). Я предполагаю, что оператор потока потока < шаблон, специализирующийся на неустойчивых указателях, но мой вопрос: почему? Какой вариант использования мотивирует это поведение?

Пример кода:

#include <iostream>
#include <cstring>

int main()
{
    char x[500];
    std::strcpy(x, "Hello world");

    int y;
    int *z = &y;

    std::cout << x << std::endl;
    std::cout << (char volatile*)x << std::endl;

    std::cout << z << std::endl;
    std::cout << (int volatile*)z << std::endl;

    return 0;
}

Вывод:

Hello world
1
0x8046b6c
1

Ответы

Ответ 1

ostream::operator<< имеет следующие перегрузки, среди прочих:

ostream& operator<< (bool val );
ostream& operator<< (const void* val );

Когда вы передаете volatile-указатель, вторая перегрузка не может применяться, потому что волатильные указатели не могут быть преобразованы в энергонезависимые без явного приведения. Однако любой указатель может быть преобразован в bool, поэтому первая перегрузка выбрана, и результат, который вы видите, равен 1 или 0.

Таким образом, настоящая причина этого не является преднамеренным решением от имени комитета по стандартизации, а просто, что стандарт не указывает перегрузку, которая принимает изменчивый указатель.

Ответ 2

Я думаю, причина в том, что волатильные указатели не могут быть явно конвертированы в void *. Это в Приложении C Стандарта, и обоснование - это безопасность типа.

Изменить: только указатели на неконстантные и энергонезависимые объекты могут быть неявно преобразован в void * Обоснование: это улучшает безопасность типов.

Итак, вместо преобразования в void * (который будет печатать в шестнадцатеричном формате) вы получите преобразование по умолчанию в bool.

Ответ 3

Не ответ

Это просто проблема с формулировкой вопроса и ответов. Проблема возникает из-за невозможности конвертировать указатели на неустойчивые объекты в указатели void, а не волатильные указатели.

Разница, которая весьма важна, заключается в том, что элемент памяти является неустойчивым. В этом вопросе указатель не является изменчивым (его можно кэшировать, и он не должен быть сброшен в память при его изменении), а скорее острая память:

int volatile * p = f();
p++;      // this does not affect the perceived state of the c++ memory model
++p;
*p = 5;   // this changes the perceived state

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

void foo( int * );

int * volatile p = f();  // 1
foo(p);                  // 2
int volatile * q = f();
//foo(q);    // error, cannot convert pointer to volatile to pointer to non-volatile
*q = 5;                  // 3
q = 0;                   // 4

В приведенном выше коде операции, отмеченные как 1 и 2, полностью переходят в память. Назначение в [1] должно быть сброшено в память. Даже если значение p находится в регистре, оно будет загружено из памяти в [2]. Операция, отмеченная [3], изменяет значение, указанное q, которое равно volatile, и будет полностью переведено в основную память, в то время как операция [4] влияет только на указатель, который не является volatile сам, и как такой подход не является частью воспринимаемого состояния модели памяти С++ и может выполняться в регистрах (обратите внимание, что компилятор может оптимизировать прочь q и выполнять операции в регистре, а p не может быть оптимизирован.

Ответ 4

Я думаю, проблема заключается не в явной перегрузке указателей на летучие типы, а в LACK перегрузки для указателей на летучие типы. Компилятор не может неявно удалять изменчивый классификатор из указателей, поэтому он проверяет доступные перегрузки, выбирает версию bool оператора < < и преобразует указатель в летучее в bool.