Гейзенбаг с преобразованием типов в C ++ 11 (XCode 6.1, clang)

Это очень странно.

OSX 10.10
LLVM 6.0
XCode 6.1

test_assert("Wierd", String{"ABC"}, "ABC" ); // claims not equal

String — это мой пользовательский класс (упаковывающий примитив Python String), и он должен пройти этот тест.

Вот test_assert с добавленным отладочным выводом:

template <typename B, typename V>
static void test_assert( std::string description, B benchmark, V value )
{
std::ostringstream full_description;

full_description << description
<< " : { " << "benchmark"      << ", " << "value"          << " }"<< " = { " << typeid(B).name() << ", " << typeid(V).name() << " }"<< " , { " << benchmark        << ", " << value            << " }";

// N2Py6StringE, PKc  i.e. Py::String and const char* (Pointer to Konst Char)
std::cout << typeid(B).name() << ", " << typeid(V).name() << std::endl;

V b_as_v{static_cast<V>(benchmark)};

// wtf? b_as_v: \352\277_\377 -- should be "ABC"std::cout << "b_as_v: " << b_as_v << std::endl; // Y

if( b_as_v == value )
std::cout << "    PASSED: " << full_description.str() << std::endl;
else
throw TestError( full_description.str() );
}

Именно это b_as_v{static_cast<V>(benchmark)}; это бросает меня, потому что, если я сделаю один шаг, это корректно приведет меня к оператору String ‘convert to const char *’, который правильно выполняет свои обязанности:

class String : Object {
explicit operator const char*()                                                    const
{
std::string s{ as_std_string() };
const char* c{ s.c_str() };

// c before return: ABC
std::cout << "c before return: " << c << std::endl; // X

return c;
}
:

Теперь это странная вещь: если строка X на месте, строка Y ничего не сообщает: ‘b_as_v:’

Удаляя его, строка Y сообщает оригинал: ‘b_as_v: \ 352 \ 277_ \ 377’

На самом деле, просто печать std::cout << std::endl; // X' для X достаточно, чтобы очистить вывод от Y (однако перемещение X ‘непосредственно перед Y восстанавливает исходное поведение).

Таким образом, похоже, что акт наблюдения изменяет возвращаемое значение.

Гейзенбаг>: |

И ни одно поведение не является желательным.

Еще одна странность заключается в том, что есть дополнительный символ Unicode, который копируется в мой буфер обмена в конце «\ 352 \ 277_ \ 377», если я копирую вставить из консоли Xcode в окно редактирования текста SO.

введите описание изображения здесь

Даже если я выберу только последний 7 он все еще копирует, даже если он не занимает пробел в консоли XCode.

(Этот дополнительный символ не появляется в SO вопросе, на самом деле его больше нет, когда я снова открываю вопрос для редактирования. Это не
символ новой строки — я тестировал вставку копий в последний символ определенной строки)

Я попытался создать тестовый сценарий, но он работает, к сожалению, как я ожидал: http://ideone.com/gbyU6Y

0

Решение

Довольно сложная настройка, но причина довольно проста:

explicit operator const char*() const
{
std::string s{ as_std_string() };
const char* c{ s.c_str() };

// c before return: ABC
std::cout << "c before return: " << c << std::endl; // X

return c;
}

Указатель возвращается std::string::c_str() указывает на std::stringвнутреннее хранилище, и поэтому может быть признано недействительным по ряду причин — уничтожение std::string объект является одним из них. Вот, c становится недействительным, как только ваша функция преобразования и s уничтожен, это означает, что висячий указатель возвращается.

Кроме того, libc ++ использует оптимизацию небольших строк, а это означает, что строка "ABC" хранится внутри std::string сам объект (в данном случае, в стеке), а не в динамически выделенном хранилище. Это значительно повышает вероятность того, что пространство, которое раньше занимало строка, может быть повторно использовано до того, как ваш код попытается ее распечатать.

3

Другие решения


По вопросам рекламы [email protected]