这很奇怪。
操作系统 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 的“转换为 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 的正前方可恢复原始行为)。
所以看起来观察行为修改了返回值。
一个海森堡 >:|
这两种行为都不是我们想要的。
另一个奇怪的是,如果我从 Xcode 的控制台复制粘贴到 SO 文本编辑窗口,那么在 '\352\277_\377' 的末尾有一个额外的 Unicode 字符复制到我的剪贴板。
即使我只选择最后一个 7
它仍然复制,即使它不占用 Xcode 控制台中的空白。
(这个额外的字符没有出现在 SO 问题上,事实上当我重新打开问题进行编辑时它不再存在。它不是 换行符——我已经在特定行的最后一个字符中测试了复制粘贴)
我曾尝试创建一个测试用例,但它的表现却如我所料:http://ideone.com/gbyU6Y
最佳答案
一个相当复杂的设置,但原因很简单:
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
对象的破坏就是其中之一。在这里,一旦您的转换函数返回并且 s
被销毁,c
就会失效,这意味着将返回一个悬空指针。
此外,libc++ 使用小字符串优化,这意味着像 "ABC"
这样短的字符串存储在 std::string
对象本身中(在此情况下,在堆栈上),而不是在动态分配的存储中。这使得过去被字符串占用的空间更有可能在您的代码尝试打印它之前被重用。
关于使用 std::string::c_str() 时的 C++11 类型转换 heisenbug,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27247633/