C++ 用C字符串初始化std::string的奇怪方法

C++ 用C字符串初始化std::string的奇怪方法,c++,c,string,cuda,C++,C,String,Cuda,在阅读nVidia CUDA源代码时,我偶然发现了以下两行代码: std::string stdDevString; stdDevString = std::string(device_string); 请注意,设备_字符串是一个字符[1024]。问题是:为什么要构造一个空的std::string,然后用一个C字符串作为参数再次构造它?为什么他们不调用std::string stdDevString=std::string(设备字符串)仅在一行中 是否存在此代码试图规避/使用

在阅读nVidia CUDA源代码时,我偶然发现了以下两行代码:

    std::string stdDevString;

    stdDevString = std::string(device_string);
请注意,设备_字符串是一个字符[1024]。问题是:为什么要构造一个空的std::string,然后用一个C字符串作为参数再次构造它?为什么他们不调用std::string stdDevString=std::string(设备字符串)仅在一行中

是否存在此代码试图规避/使用的隐藏字符串初始化行为?是为了确保stdDevString中的C字符串在任何情况下都保持null终止?因为据我所知,将std::string初始化为不以null结尾的C字符串仍然会出现问题

为什么他们不调用std::string stdDevString=std::string(设备字符串)仅在一行中

他们这样做没有充分的理由。给定
std::string::string(const char*)
构造函数,您只需使用以下任意一种:

std::string stdDevString = device_string;
std::string stdDevString(device_string);
std::string stdDevString{device_string}; // C++11 { } syntax
两步默认构造然后分配只是(糟糕的)程序员风格或疏忽。如果没有优化,它确实会进行一些不必要的构建,但这仍然相当便宜。它很可能通过优化而消除。没什么大不了的-我怀疑我是否会费心在代码评审中提到它,除非它是在一个性能非常敏感的领域,但绝对最好推迟声明变量,直到有一个有用的初始值可用来构造它们,将所有变量都放在一个地方:它不仅不容易出错,而且可以交叉引用,但它最小化了变量的范围,简化了使用变量的推理

是为了确保
stdDevString
中的C字符串无论发生什么情况都以null结尾

不,这没什么区别。由于C++11,无论使用哪种构造函数,
stdDevString
中的内部缓冲区都将保持NUL终止,而对于C++03,不一定终止-请参见下面C++03详细信息的专用标题-但无论如何进行构造/分配,都无法保证

因为据我所知,将
std::string
初始化为非空终止的C字符串仍然会出现问题

您是对的-您列出的任何构造选项都只会将ASCIIZ文本复制到
std::string
-考虑到第一个NUL(
'\0'
)终止符。如果字符数组不是NUL终止的,则会出现问题

(对于
std::string
中的缓冲区是否保持NUL终止,这是一个单独的问题——上面已经讨论过)

请注意,有一个单独的
字符串(const char*,size_type)
构造函数,它可以创建带有嵌入NUL的字符串,并且不会尝试读取更多的内容(构造函数(4))

C++03 std::字符串不保证在内部以NUL终止 无论以何种方式构造和初始化
std::string
在C++11之前,标准不要求它在字符串的缓冲区内以NUL结尾
std::string
最好被想象为包含一组潜在的不可打印字符(松散地说,是ftp/文件I/O意义上的二进制字符),从地址
data()
开始,扩展为
size()
字符。所以,如果你有:

std::string x("help");
x[4];  // undefined behaviour: only [0]..[3] are safe
x.at(4); // will throw rather than return '\0'
x.data()[4]; // undefined behaviour, equivalent to x[4] above
x.c_str()[4]; // safely returns '\0', (perhaps because a NUL was always
              // at x[4], one was just added, or a new NUL-terminated
              // buffer was just prepared - in which case data() may
              // or may not start returning it too)
请注意,std::string API需要
c_str()
返回指向NUL终止值的指针。为此,它可以:

  • 始终主动在字符串缓冲区的末尾保留一个额外的
    NUL
    (在这种情况下,
    data[5]
    恰好在该实现上是安全的,但如果实现发生更改或代码被移植到另一个标准库实现等,则代码可能会中断。)
  • 反应性地等待,直到调用
    c_str()
    ,然后:

    • 如果它在当前地址有足够的容量(即
      data()
      ),则附加一个
      NUL
      ,并返回与
      data()
      返回的指针值相同的指针值
    • 否则,分配一个新的、更大的缓冲区,复制数据,
      NUL
      终止它,并返回指向它的指针(通常但也可以选择此缓冲区将替换将被删除的旧缓冲区,这样,随后立即调用
      data()
      将返回由
      c_str()
      返回的相同指针)

我认为这相当于写作:

std::string stdDevString = std::string(device_string);
或者更简单一点:

std::string stdDevString = device_string;

一旦创建了std::string,它就包含了C字符串中数据的私有副本。

在我看来就像一件艺术品。也许中间还有其他代码,后来被删除了,有人懒得把剩下的两行合并成一行。

我认为把这看作是糟糕的编码是无知的。如果我们假设这个字符串是在文件范围内分配的,或者是作为一个静态变量分配的,那么它可能是一个很好的编码

在对具有非易失性内存的嵌入式系统进行C++编程时,有很多理由希望避免静态初始化:主要原因是程序开始时增加了很多开销代码,所有这些变量都被初始化。如果它们是类的实例,则将调用构造函数

这将导致程序执行开始时出现延迟峰值。您不希望出现这种工作负载高峰,因为在启动程序时有更重要的任务要做,比如设置各种硬件

为了避免这种情况,您通常会在编译器中启用一个选项来删除此类静态初始化,然后以这样一种方式编写代码:不初始化静态/全局变量,而是在运行时设置它们


在这样的系统中,OP发布的代码是正确的方法。

Dunno,对我来说就像对你一样多余。事实上,它可能初始化了两次。但是,话说回来,也许