2016-08-29 40 views
4

所以我有一个std::string并且有一个函数,它需要char*并写入它。由于std::string::c_str()std::string::data()返回const char*,我不能使用它们。所以我分配了一个临时缓冲区,调用一个函数并将其复制到std::string直接写入std :: string的char *缓冲区

现在我打算使用大量的信息,复制这个缓冲区会有明显的影响,我想避免它。

有人建议使用&str.front()&str[0]但它会调用未定义的行为吗?

+5

“* C++ 17在'std :: string'中添加了非const的data()',但它仍然表示不能修改缓冲区。它在哪里说呢?在访问'&std [0]''&str.font()'之前访问 – ildjarn

+0

,...确保有内存,_ie_表示'str.size()> 0'。如果你只是实例化'str',使用'str.resize()' – Garf365

+1

@ildjam看起来我完全误解了那篇论文。 '.data()'应该可以工作。 – Lyberta

回答

15

C++ 98/03

不可能。字符串可以在写入时复制,因此需要处理所有的读写操作。

C++一十四分之一十一

在[string.require]:

basic_string对象的炭状物体将被连续地存储。也就是说,对于任何basic_string 对象s,身份&*(s.begin() + n) == &*s.begin() + n应保持所有n的值,例如0 <= n < s.size()

因此&str.front()&str[0]应该工作。

C++ 17

str.data()&str.front()&str[0]工作。

Here它说:

charT* data() noexcept;

返回:指针p这样p + i == &operator[](i)[0, size()]每个i

复杂性:时间不变。

要求:程序不得更改存储的值at p + size()

非常量.data()只是起作用。

recent draft具有用于.front()措辞如下:

const charT& front() const;

charT& front();

需要:!empty()

影响:相当于operator[](0)

并为operator[]如下:

const_reference operator[](size_type pos) const;

reference operator[](size_type pos);

要求:pos <= size()

返回:*(begin() + pos) if pos < size()。否则,返回对charT类型的对象的引用,其值为charT(),其中修改对象会导致未定义的行为。

抛出:无。

复杂性:时间不变。

所以它使用迭代器算法。所以我们需要检查关于迭代器的信息。 Here它说:

3 basic_string是一个连续的容器([container.requirements.general])。

因此,我们需要去here

甲邻接容器是支持随机访问迭代([random.access.iterators]),其成员类型iteratorconst_iterator是连续的迭代器(容器[iterator.requirements.general])。

然后here

迭代器进一步满足的是,对于整数值n和提领迭代值a(a + n)要求,*(a + n)相当于*(addressof(*a) + n),被称为连续的迭代器。

显然,连续迭代器是一个C++ 17功能,它被添加到thesepapers中。

要求可以改写为:

assert(*(a + n) == *(&*a + n)); 

因此,在第二部分中,我们解引用迭代器,然后取值的地址,它指向,然后做一个指针运算就可以了,解引用它和它的与递增迭代器然后解除引用相同。这意味着连续的迭代器指向内存,每个值紧挨着一个存储,因此是连续的。由于函数char*需要连续的内存,因此可以将&str.front()&str[0]的结果传递给这些函数。

+0

很好的答案。只是一些补充说:'std :: string'是一个连续的内存容器,只有自C++ 11以来。所以你可以使用安全的'&str [0]',因为只有C++ 11(之前,它是std lib实现特定的)。在C++ 11之前,你必须使用一个临时的'std :: vector'并将它复制到'std :: string'或者用它来实例化一个'std :: string' – Garf365

+1

鉴于'operator []的定义“说”修改对象导致未定义的行为。“我认为你的回答是错误的。我认为这是标准中的缺陷。 –

+0

@ Garf365:实际上,没有标准库*没有为std :: string提供连续的内存(因为C++ 98),所以它一直是安全的,C++ 11说它始终是安全的。 –

0

您可以简单地使用&s[0]作为非空字符串。这给你一个指针到缓冲区

当你用它来放的ň人物那里string的长度(不仅仅是容量)必须至少ň事先字符串的开始,因为没有破坏数据就无法调整它。

即,使用可以是这样的:

auto foo(int const n) 
    -> string 
{ 
    if(n <= 0) { return ""; } 

    string result(n, '#'); // # is an arbitrary fill character. 
    int const n_stored = some_api_function(&result[0], n); 
    assert(n_stored <= n); 
    result.resize(n_stored); 
    return result; 
} 

因为C++ 11这种做法已正式工作。在此之前,在C++ 98和C++ 03中,缓冲区没有正式保证是连续的。然而,实际上这种方法自从C++ 98开始工作以来,第一个标准是可以在C++ 11中采用连续缓冲区要求的原因(在利勒哈默会议中增加了它,我认为这是2005年)是没有现成的标准库实现与非连续的字符串缓冲区。


关于

C++ 17加入添加非const data()std::string但它仍然说,你不能修改缓冲区。

我不知道有任何这样的措辞,因为这将打败非const的目的data()我怀疑这种说法是正确的。


关于

现在我计划的信息,并复制该缓冲区大量的工作将有一个显着的影响,我想避免它。

如果复制缓冲区有明显的影响,那么您应该避免无意中复制std::string

一种方法是将其包装在不可复制的类中。

0

我不知道你打算与string做什么,但如果
所有你需要的是字符的缓冲自动释放自己的内存,
然后我通常使用vector<char>vector<int>或任何类型
您需要的缓冲区。

随着v作为载体,它保证&v[0]
连续的内存,你可以作为缓冲使用。

+0

自从C++ 11和之前的版本中,std :: string也是如此,就像所说的MatinBonner在其他答案的评论中一样,虽然这种行为是非标准的,但大多数标准lib实现了'std :: string'作为一个连续的记忆 – Garf365