如果我肯定要复制字符串,如何有效地将它传递给函数?C++20

vic*_*ict 5 c++ std stdstring string-view c++20

我想知道如何正确处理函数中的字符串输入,如果我知道我将必须在其中创建一个副本以将其放入容器中。

我想这样做:

void foo(const string& s){
    container.push(s);
}
Run Code Online (Sandbox Code Playgroud)

但是如果我传递一个 char* 字符串,我将制作 2 个副本,首先是在调用 foo 时,然后是在将其传递到容器时。

我的第二个想法是使用 string_view,正如人们常说的,如果你可以使用视图,你就应该使用。所以我的函数的第二个版本如下所示:

void foo2(string_view s){
    container.push(string(s));
}
Run Code Online (Sandbox Code Playgroud)

好的,现在无论给我什么类型的字符串,我都会只制作一个副本。但后来我开始思考,为什么我不能接受一个字符串作为函数的参数,像这样简化它:

void foo3(string s){
    container.push(std::move(s));
}
Run Code Online (Sandbox Code Playgroud)

但现在我必须确保我的容器正确利用移动语义,这样它最终不会制作另一个副本!由于此容器对象是模板化类型,因此意味着使用完美转发等,这本身就是大量工作。

目前我不知道该选择哪个选项,所以想请教您一些建议。我还觉得我想得太多了,因为这最终可能不会对性能产生太大影响,但我想做得正确。提前致谢。

bol*_*lov 8

你知道你std::string最终肯定需要一个。不是类似字符串的东西,而是一个std::string. 在这种情况下,将 以外的任何东西作为参数是没有意义的std::string。也有例外,例如,如果您使用所有公开的接口,std::string_view那么在这里这样做也可能有意义。

现在的选择是const std::string&(左值引用)、std::string&&(右值引用)和std::string(值)。在这种情况下,您知道需要一个新对象传递到低于推荐对象的级别:value +std::move

这边走:

  • 如果用户有除 a 之外的其他内容,std::string您将获得 1 次转化 + 1 次移动
  • 如果用户有,std::string您将获得 1 个副本 + 1 个移动(如果用户std::move在呼叫站点这样做,则为 1 个移动 + 1 个移动)。

但现在我必须确保我的容器正确利用移动语义

不,不是“现在”。这不应该是您开始考虑容器移动语义的点。容器很可能应该具有适当的移动语义,只是因为它是……嗯,容器。当您可以移动 5k 元素时,您不想复制它们。


Ted*_*gmo 5

第四个选项是使用完美转发并将参数按foo 原样传递给容器的emplace/emplace_back函数:

template<class... Args>
requires std::constructible_from<std::string, Args...>
void foo4(Args&&... args){ // a forwarding reference pack
    container.emplace(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)
  1. 如果您将临时值传递std::stringfoo4,它将使用std::string移动构造函数。没有其他的。
  2. 如果将左值引用传递给foo4,它将使用std::string复制构造函数。没有其他的。
  3. 如果您传递可用于构造 a 的其他参数std::string(例如 a )std::string_view,它将在容器中就地构造。甚至不需要复制或移动。

演示


通过手动创建避免不必要的实例化所需的重载,您可以同样出色地处理上述前两种情况。

void foo4(const std::string& f) { container.push(f); } // one copy
void foo4(std::string&& f) { container.push(std::move(f)); } // one move
Run Code Online (Sandbox Code Playgroud)

然而,第三种情况不会同样有效。如果您不关心或不想支持第三种情况,您可以选择两个重载来模仿复制构造函数/移动构造函数对的功能。既然您询问如何 “有效地传递字符串”,我认为添加重载是一个很小的代价。

  • @alagner是的,`constructible_from`可以帮助使错误消息变得更好。 (3认同)