C++11 为什么';t string::data()是否提供可变字符*?
在C++11 为什么';t string::data()是否提供可变字符*?,c++11,c++,string,containers,c-strings,c++17,C++11,C++,String,Containers,C Strings,C++17,在数组中,字符串和向量都得到了数据方法,该方法: 返回指向用作元素存储的基础数组的指针。指针使范围[data();data()+size())始终为有效范围,即使容器为空。 [] 此方法在所有适用容器的可变和const版本中提供,例如: T* vector<T>::data(); const T* vector<T>::data() const; 这里发生了什么?当char*string::data()会很有帮助时,为什么string会被短改?我认为这一限制来自于(20
数组
中,字符串
和向量
都得到了数据
方法,该方法:
返回指向用作元素存储的基础数组的指针。指针使范围[data()
;data()+size()
)始终为有效范围,即使容器为空。
[]
此方法在所有适用容器的可变和const
版本中提供,例如:
T* vector<T>::data();
const T* vector<T>::data() const;
这里发生了什么?当
char*string::data()
会很有帮助时,为什么string
会被短改?我认为这一限制来自于(2011年之前)的时代,std::basic_string
不必将其内部缓冲区存储为连续字节数组
而所有其他(std::vector
等)必须按照2003年的标准将其元素存储为连续序列;因此数据
可以轻松返回可变T*
,因为迭代等没有问题
如果std::basic_string
返回一个可变的char*
,这意味着您可以将该char*
视为有效的C字符串,并执行类似于strcpy
的C字符串操作,如果字符串没有连续分配,则很容易转向未定义的行为
C++11标准增加了basic_string
必须作为连续字节数组实现的规则。不用说,您可以使用&str[0]的老技巧来解决这个问题
简单的回答是,它确实提供了。这对于类似的情况非常重要,因此,要获得对底层C字符串的可变访问权,我现在可以执行以下操作:
auto foo = "lorem ipsum"s;
for(auto i = data(foo); *i != '\0'; ++i) ++(*i);
出于历史目的,值得记录
string
的发展,其基础是:在访问string
的过程中,一个新的要求使得底层缓冲区成为可能,即它的元素被连续存储,以便对于任何给定的string s
:
&*(s.begin()+n)==&*s.begin()+n
对于[0
,s.size()
)中的任何n
,或者,可以将指向s[0]
的指针传递给需要指向图表[
数组的第一个元素的函数
可以通过各种方法获得对这个新要求的底层C字符串的可变访问,例如:&s.front()
,&s[0]
,或&s.first()
但回到原来的问题,这将避免使用其中一个选项的负担:为什么没有以char*string::data()的形式提供对string
的底层缓冲区的访问?
要回答这个问题,需要注意T*数组::数据()
和T*向量::数据()
是的一个附加要求。对于其他连续容器,如deque
,没有产生任何附加要求。当然,对于字符串
,也没有附加要求,事实上,该要求是新的。在此之前,该要求已经存在。尽管它明确地不能保证指向对于任何底层缓冲区,这是从字符串
获取常量字符*
的唯一方法:
返回的数组不需要以null结尾
这意味着
string
没有被“短改”在向数据
访问器的转换过程中,它根本不包括在内,因此只有常量
数据
访问或字符串
之前拥有的访问被保留下来。有一些需要直接写入字符串
的底层缓冲区的内容,我们已经讨论过了,并将其记录为一个问题它是:&s[0]
,用于非空字符串。我认为,如果您需要通过数据
修改字符串,您可能正在寻找向量
。但也有一些例外情况。@black我广泛使用向量
,每次都很遗憾,后来我将字符复制回字符串
。什么实际上我想要的是一个带有可修改缓冲区的字符串
方法将通过鼓励开发人员将string
对象用作字节缓冲区来击败它的封装。因此,这将是一个倒退的步骤。根据这里的说法:std::string::data
自C++98以来就已经存在。它还谈到了我在这里提出的相同问题。@JonathanMee我肯定有在很多情况下,我确信这是一件好事:-)我想至少方法data()
明确表达了意图,而&s[0]
是神秘的。所以,好吧,我被说服了。@JonathanMee如果因为缺少合适的操纵器而缺少ostream功能,那么修复标准使其提供一个不是更好吗?在我看来,将格式化数据发送到流中可以通过一种更为封装的方式来实现,而不是求助于sprintf
在主线代码中。作为自定义实用程序函数的一个实现细节,它更容易被原谅,IMHO。@JonathanMee总的来说,我同意你的看法。提供一个可变的数据()
比用&s[0]
或addressof(*begin(s))破解它要不那么神秘
。我想我们刚刚创造了历史。我刚刚在互联网上公开改变了我的观点:-)
auto foo = "lorem ipsum"s;
for(auto i = data(foo); *i != '\0'; ++i) ++(*i);