为什么 std::stringstream 不能与 std::string_view 一起使用?

5

std::stringstream初始化构造函数接受 const string& 作为参数:

explicit stringstream (const string& str,
                       ios_base::openmode which = ios_base::in | ios_base::out);

在 C++98 中,这个接口是合理的,但自从 C++17 开始,我们有了std::string_view作为代替字符串的更便宜的选择。而 std::stringstream 类不修改它所接受的字符串,也不拥有该字符串,并且不要求它以 null 结尾。那么为什么不添加另一个构造函数重载,接受std::string_view呢?是否存在任何障碍使得这种解决方案不可能(或不合理),而采用 Boost::Iostreams 等替代方案呢?


5
可能是因为它没有被添加。没有人撰写提案,所以它没有完成。还有许多其他地方也没有完成这个任务,例如异常构造函数。 - user1143634
@StaceyGirl,那看起来是一个显而易见的答案,但奇怪的是,据我所知,即使在C++23中也没有提出任何解决该问题的建议。 - Dmitry Kuzminov
2个回答

9

当前情况下(即我们接近C++23时),这并没有太多意义。

由于你使用了stringstream而不是其他更专用的版本,有两种可能性:要么你打算写入流中,要么你不打算写入。

如果你不打算向流中写入内容,则不需要复制数据。所有形式的stringstream都拥有其作用的字符,因此应尽量避免复制。你可以使用C++23中的类型ispanstream(替代了旧的strstream)。它需要一个span<const CharT>,但string_view 也应与其中一个ispanstream的构造函数兼容

如果你打算向流中写入内容,则需要将数据复制到stringstream中。但你不需要执行两次复制。因此,C++20为stringstream提供了一个从std::string移动构造函数。参见此处的第6个构造函数

explicit basic_stringstream( std::basic_string<CharT,Traits,Allocator>&& str,
                             std::ios_base::openmode mode = 
                             std::ios_base::in | std::ios_base::out );
  1. 使用 str 进行基础字符串设备的移动构造。基础的 basic_stringbuf 对象将会以 basic_stringbuf<Char,Traits,Allocator>(std::move(str), mode) 进行构造。

并且,由于 std::string 可以从 string_view 进行构造,因此将 std::string_view 传递到 std::stringstream 构造函数中将使用此移动构造函数重载,可以最小化复制。

因此,实际上不需要一个特定于 string_view 的构造函数。


0

如何在C++11或更高版本中高效地(如果您喜欢,可以使用std::move())从std::stringstd::string_view或C字符串构造一个std::stringstream

快速摘要

std::string str("Hello ");                            // std::string
constexpr char c_str[] = "Hey and how are you ";      // C-string
std::string_view sv(c_str);                           // std::string_view

std::stringstream ss1(str);                           // from a std::string
std::stringstream ss3(std::move(std::string(c_str))); // from a C-string
std::stringstream ss2(std::move(std::string(sv)));    // from a std::string_view

详细信息

我刚刚使用C++20的移动构造函数(构造函数#6)定义和解释,从https://en.cppreference.com/w/cpp/io/basic_stringstream/basic_stringstream更新了@Nicol Bolas的被接受的答案。 然而,如果您正在使用C++11,则仍然可以通过调用std::move()来获得此效率提升(据我所知)。

自C++11以来,无论是否显式调用std::move(),以下3种技术都可以用于构造std::stringstream因此,没有必要从std::string_view有额外的构造函数,因为您已经可以从std::stringstd::string_view或C字符串(char数组)构造std::stringstream,如下所示。

你可以在我的 eRCaGuy_hello_world 仓库中的 stringstream_initialize_from_std_string__string_view__and_c_string.cpp 文件中玩一些测试示例。

std::string str("Hello ");                          // std::string
constexpr char c_str[] = "Hey and how are you ";    // C-string
std::string_view sv(c_str);                         // std::string_view

// 1. Construct a `std::stringstream` from a `std::string`. This is
// constructor #3 from the link below: reference page for the
// `std::stringstream` constructors:
// https://en.cppreference.com/w/cpp/io/basic_stringstream/basic_stringstream
// - Open in mode `std::ios_base::app` as well in order to **append** all new
//   writes to the end of the stream! See link above **and**:
//   https://dev59.com/kmoy5IYBdhLWcg3wFqFT#8786212
std::stringstream ss1(str, 
    std::ios_base::in | std::ios_base::out | std::ios_base::app);
ss1 << "world.\n";  // since `std::ios_base::app` was used above, this 
                    // **appends** rather than **overwrites** the data in 
                    // the stringstream.
std::cout << ss1.str() << "\n";

// 2. Construct a `std::stringstream` from a `std::string_view`. This is also
// constructor #3 from the link above (passing in a `std::string), but we must
// first construct a `std::string` from the `std::string_view`. We are using
// constructor #10 from the `std::string` constructors shown here to create a
// `std::string` from a `std::string_view`:
// https://en.cppreference.com/w/cpp/string/basic_string/basic_string
// - See also the note about the `std::ios_base::app` mode above.
std::stringstream ss2b(std::move(std::string(sv)),
    std::ios_base::in | std::ios_base::out | std::ios_base::app);
ss2b << "today?\n";
std::cout << ss2b.str() << "\n";

// 3. Construct a `std::stringstream` from a C-string. This is also
// constructor #3 from the link above (passing in a `std::string), but we must
// first construct a `std::string` from the C-string. We are using
// constructor #5 from the `std::string` constructors shown here to create a
// `std::string` from a C-string (`const char*`):
// https://en.cppreference.com/w/cpp/string/basic_string/basic_string
// - See also the note about the `std::ios_base::app` mode above. Note that the
//   C-string is used to automatically, implicitly construct a `std::string`
//   here, I believe.
//
// implicit construction of `std::string` from `c_str`
std::stringstream ss3c(std::move(c_str),
    std::ios_base::in | std::ios_base::out | std::ios_base::app);
ss3c << "doing?\n";
std::cout << ss3c.str();
// explicit construction of `std::string` from `c_str`
std::stringstream ss3d(std::move(std::string(c_str)),
    std::ios_base::in | std::ios_base::out | std::ios_base::app);
ss3d << "doing?\n";
std::cout << ss3d.str();

示例输出:

Hello world.
Hey and how are you today?
Hey and how are you doing?
Hey and how are you doing?

参考文献

  1. std::stringstream 的构造函数:https://en.cppreference.com/w/cpp/io/basic_stringstream/basic_stringstream
  2. 从这里显示的 std::string 构造函数中的构造函数#10,用于从 std::string_view 创建一个 std::stringhttps://en.cppreference.com/w/cpp/string/basic_string/basic_string
    1. 和从 C 字符串(const char*)创建一个 std::stringstd::string 构造函数中的构造函数#5
  3. 如何向 stringstream 类型对象附加内容?

std::stringstream ss2(std::move(std::string(str))); // 从 std::string_view 你是不是想用 sv 而不是 str - Karl Knechtel
这两个 std::move 是不必要的:std::stringstream ss3(std::move(std::string(c_str))); 对于 rvalue,你不需要这样做。只有 lvalue 需要被 move - digito_evo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接