从 const char * 隐式构造 const std::string 是否有效?
Is implicit construction of `const std::string` from `const char *` efficient?
像许多人一样,我习惯于将新的字符串函数编写为 const std::string &
的函数。优点是效率(您可以传递现有的 std::string
对象而不会产生 copying/moving 的开销)和 flexibility/readability(如果您只有一个 const char *
,您可以传递它并拥有构造是隐式完成的,没有用显式 std::string
构造混淆你的代码):
#include <string>
#include <iostream>
unsigned int LengthOfStringlikeObject(const std::string & s)
{
return s.length();
}
int main(int argc, const char * argv[])
{
unsigned int n = LengthOfStringlikeObject(argv[0]);
std::cout << "'" << argv[0] << "' has " << n << " characters\n";
}
我的目标是编写能够有效处理长字符串的高效跨平台代码。我的问题是,隐式构造期间会发生什么?是否可以保证不会复制该字符串?令我印象深刻的是,因为一切都是 const
,所以不需要复制——只需要一个围绕现有指针的薄 STL 包装器——但我不确定我应该如何依赖编译器和平台来期望这种行为成为。始终显式编写函数的两个版本是否更安全,一个用于 const std::string &
,一个用于 const char *
?
如果您将 const char*
传递给需要 std::string
的内容,无论是否引用,都会构造一个字符串。如果您将它发送给引用并警告存在隐式临时对象,编译器甚至可能会抱怨。
现在这可能会被编译器优化,而且一些实现不会为小字符串分配内存。编译器也可能在内部优化它以使用 C++17 string_view
。它基本上取决于您将对代码中的字符串执行的操作。如果你只使用常量成员函数,一个聪明的编译器可能会优化掉。
但这取决于实施并且不受您的控制。如果您想接管,可以明确使用 std::string_view
。
It strikes me that, because everything is const, copying is not
necessary—a thin STL wrapper around the existing pointer is all that's
needed
我不认为这个假设是正确的。仅仅因为你有一个指向 const 的指针,并不意味着基础值不能改变。它仅表示无法通过该指针更改该值。该指针可能指向可以随时更改的非常量存储。
因此,库必须制作自己的副本(以提供 "correct" 字符串可观察行为)。对 libstdc++ 的快速回顾表明它总是制作一个副本。来自 char*
的构造不是内联的,因此如果没有静态链接和 LTO 就无法对其进行优化。
虽然极其琐碎的静态链接程序可能会使用 LTO 优化副本(我无法重现这一点),但我认为通常不太可能执行此优化(特别是考虑别名规则char*
)。 g++ 甚至不对字符串文字执行此优化。
如果你不想复制,那么string_view
就是你想要的。
然而,好处也随之而来。具体来说,您必须确保您传递的存储持续 "long enough".
对于字符串文字,这没问题。对于 argv[0]
,这几乎肯定不是问题。对于任意字符序列,则需要考虑它们。
但你可以这样写:
unsigned int LengthOfStringlikeObject(std::string_view sv)
{
return sv.length();
}
然后用 string
或 const char *
调用它就可以了。
像许多人一样,我习惯于将新的字符串函数编写为 const std::string &
的函数。优点是效率(您可以传递现有的 std::string
对象而不会产生 copying/moving 的开销)和 flexibility/readability(如果您只有一个 const char *
,您可以传递它并拥有构造是隐式完成的,没有用显式 std::string
构造混淆你的代码):
#include <string>
#include <iostream>
unsigned int LengthOfStringlikeObject(const std::string & s)
{
return s.length();
}
int main(int argc, const char * argv[])
{
unsigned int n = LengthOfStringlikeObject(argv[0]);
std::cout << "'" << argv[0] << "' has " << n << " characters\n";
}
我的目标是编写能够有效处理长字符串的高效跨平台代码。我的问题是,隐式构造期间会发生什么?是否可以保证不会复制该字符串?令我印象深刻的是,因为一切都是 const
,所以不需要复制——只需要一个围绕现有指针的薄 STL 包装器——但我不确定我应该如何依赖编译器和平台来期望这种行为成为。始终显式编写函数的两个版本是否更安全,一个用于 const std::string &
,一个用于 const char *
?
如果您将 const char*
传递给需要 std::string
的内容,无论是否引用,都会构造一个字符串。如果您将它发送给引用并警告存在隐式临时对象,编译器甚至可能会抱怨。
现在这可能会被编译器优化,而且一些实现不会为小字符串分配内存。编译器也可能在内部优化它以使用 C++17 string_view
。它基本上取决于您将对代码中的字符串执行的操作。如果你只使用常量成员函数,一个聪明的编译器可能会优化掉。
但这取决于实施并且不受您的控制。如果您想接管,可以明确使用 std::string_view
。
It strikes me that, because everything is const, copying is not necessary—a thin STL wrapper around the existing pointer is all that's needed
我不认为这个假设是正确的。仅仅因为你有一个指向 const 的指针,并不意味着基础值不能改变。它仅表示无法通过该指针更改该值。该指针可能指向可以随时更改的非常量存储。
因此,库必须制作自己的副本(以提供 "correct" 字符串可观察行为)。对 libstdc++ 的快速回顾表明它总是制作一个副本。来自 char*
的构造不是内联的,因此如果没有静态链接和 LTO 就无法对其进行优化。
虽然极其琐碎的静态链接程序可能会使用 LTO 优化副本(我无法重现这一点),但我认为通常不太可能执行此优化(特别是考虑别名规则char*
)。 g++ 甚至不对字符串文字执行此优化。
如果你不想复制,那么string_view
就是你想要的。
然而,好处也随之而来。具体来说,您必须确保您传递的存储持续 "long enough".
对于字符串文字,这没问题。对于 argv[0]
,这几乎肯定不是问题。对于任意字符序列,则需要考虑它们。
但你可以这样写:
unsigned int LengthOfStringlikeObject(std::string_view sv)
{
return sv.length();
}
然后用 string
或 const char *
调用它就可以了。