使用 std::string::c_str() 时的 C++11 类型转换 heisenbug

标签 c++ c++11 type-conversion xcode6

这很奇怪。

操作系统 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 字符复制到我的剪贴板。

enter image description here

即使我只选择最后一个 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/

相关文章:

c++ - 将虚拟方法放在可复制类型上是否是个好主意?

c++ - assert() 是否充当 Release模式下的身份函数?

c++ - 功能对象的包装器,用于更改参数和返回值类型

c++ - 数据类型之间的隐式转换

objective-c - 无法将类型 'Swift._SwiftDeferredNSDictionary<Swift.String, Swift.Double>' (0x11b1b84f8) 的值转换为 'NSMutableDictionary'

c++ - Omnet 和 Inet 链接错误 : undefined reference to typinfo

c++ - 是否可以创建模板模板参数列表?

c++ - 哈希函数无法正常工作

c++ - %llx 格式说明符 : invalid warning?

java - 无符号 16 位和 64 位整数