Почему gcc 4.7.0 дает мне ошибку в этом коде, а онлайн ideone (gcc 4.5.1) - нет? - PullRequest
2 голосов
/ 19 января 2012

У меня есть следующий код (работает только на gcc):

#include <iostream>
#include <cstdlib>
#include <string>
#include <typeinfo>
#include <cxxabi.h>

const std::string demangle (const char* name) {

    int status = -4;
    char* res = abi::__cxa_demangle(name, 0, 0, &status);
    const char* const demangled_name = (status == 0) ? res : name;
    std::string ret_val(demangled_name);
    std::free(res);

    return ret_val;
}

template <typename T>
const std::string getname (T x)
{
    return demangle(typeid(x).name());
}

int main()
{
   std::add_const<int>::type *p = static_cast<const int *>(0); 
   std::cout << getname(*p) << std::endl;
}

На моем локальном компьютере (с gcc 4.7.0 (экспериментальный) он дает сбой (запуск с gdb дает ошибку по умолчанию). Однако на ideone.com он печатает «int», как и ожидалось. Вот, ссылка на пример . Кроме того, избавление от шаблона и вызов demangle(typeid(x).name()) напрямую решают проблему, так что не так с шаблоном?

EDIT Я забыл включить заголовок type_traits (doh!), Который устранил проблему, однако я все еще хотел бы знать, что происходит немного лучше.

Ответы [ 2 ]

3 голосов
/ 19 января 2012

Здесь есть небольшая проблема: это проблема оценки.

Обычно разыменование нулевого указателя равно Неопределенное поведение ;однако есть угловой случай (по крайней мере для gcc), который вызывает typeid(*p), где p является нулем, является допустимым выражением.

Поэтому проблема здесь заключается в вашем уровне косвенности:

int* p = 0;

getname(*p);       // p is null, *p invokes undefined behavior

typeid(*p).name(); // p is null, but it's probably okay in `typeid`
                   // because it's an unevaluated operand here

Для тех, кто интересуется углубленным изучением, выражение typeid подробно описано в §5.2.8 (C ++ 11).

2 / Когда typeid применяется к выражению glvalue, тип которого является полиморфным типом класса (10.3), результат ссылается на объект std::type_info, представляющий тип самого производного объекта (1.8)(то есть динамический тип), к которому относится glvalue.Если выражение glvalue получено путем применения к указателю унарного оператора *, а указатель является нулевым значением указателя (4.10), выражение typeid выдает исключение std::bad_typeid (18.7.3).

Хорошо, поэтому typeid(*p) определяется, если p указывает на полиморфный класс ... и в этом случае он выдает.

3 / Когдаtypeid применяется к выражению, отличному от glvalue типа полиморфного класса, результат относится к объекту std::type_info, представляющему статический тип выражения.Преобразования Lvalue-to-rvalue (4.1), массив-указатель (4.2) и функция-указатель (4.3) не применяются к выражению.Если тип выражения является типом класса, класс должен быть полностью определен.Выражение является неоцененным операндом (раздел 5).

Интересно, что нет упоминания нулевых указателей для неполиморфных классов.Я подозреваю, что неоцененный операнд это то, что позволяет это, но еще не нашел цитату.

6 / Если заголовок <typeinfo> (18.7.1) не включено до использования typeid, программа неработоспособна.

Диагностика не требуется.По крайней мере, на gcc, я думаю, что оно выдает предупреждение.

3 голосов
/ 19 января 2012
std::add_const<int>::type *p = static_cast<const int *>(0); 

p является нулевым указателем, и разыменование его (т. Е. *p) вызывает неопределенное поведение (UB).Вам повезло, что это дает сегфо.А поскольку это на самом деле UB, все компиляторы (и все версии) могут не давать segfault, потому что именно это означает UB, т. Е. Может произойти что угодно.вместо:

std::cout << getname(int()) << std::endl;
...