【发布时间】:2022-01-18 18:20:49
【问题描述】:
我有以下代码:
#include <variant>
#include <string>
#include <iostream>
using Variant = std::variant<double, std::string>;
// helper type for the visitor
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
std::string string_from(const Variant& v)
{
return std::visit(overloaded {
[](const double arg) { return std::to_string(arg); },
[](const std::string& arg) { return arg; },
}, v);
}
int main()
{
Variant v1 {"Hello"};
Variant v2 {1.23};
std::cout << string_from(v1) << '\n';
std::cout << string_from(v2) << '\n';
return 0;
}
我有一个名为string_from() 的函数,它接受一个变体并将其内部值转换为字符串。
变体可以包含std::string 或double。
如果是std::string,我就直接退货。
如果是double,我会从double 创建一个std::string,然后将其返回。
问题是,我不喜欢在字符串变体的情况下返回std::string 的副本。理想情况下,我会返回 std::string_view 或其他类型的字符串观察者。
但是,我无法返回 std::string_view,因为在双变量的情况下,我需要创建一个新的临时 std::string 并且 std::string_view 是非拥有的。
出于同样的原因,我无法返回 std::string&。
我想知道是否有办法优化代码,以便在字符串变体的情况下避免复制。
请注意,在我的实际用例中,我经常从字符串变体中获取字符串,但很少从双变体中获取字符串。
但我仍然希望能够从双变量中获得std::string。
另外,在我的实际用例中,我通常只是观察字符串,所以我并不需要每次都复制。 std::string_view 或其他一些字符串观察器在这种情况下是完美的,但由于上述原因,这是不可能的。
我考虑了几种可能的解决方案,但我不喜欢其中任何一个:
-
返回
char*而不是std::string并在double的情况下将c 字符串分配到堆上的某处。在这种情况下,我还需要将整个内容包装在一个拥有堆分配字符串的类中以避免内存泄漏。 -
返回带有自定义删除器的
std::unique_ptr<std::string>,该删除器将清理堆分配的字符串,但如果字符串驻留在变体中,则不会执行任何操作。不确定如何实现这个自定义删除器。 -
更改变体,使其包含
std::shared_ptr<std::string>。然后,当我需要来自字符串变体的字符串时,我只返回一个 shared_ptr 的副本,当我需要来自双变体的字符串时,我调用std::make_shared()。
第三种解决方案有一个固有的问题:std::string 不再驻留在变体中,这意味着追逐指针并失去性能。
您能否针对此问题提出任何其他解决方案?每次调用函数时都比复制std::string 性能更好。
【问题讨论】:
-
你真的需要存储
double吗?为什么不总是将double转换为std::string并使用std::string而不是变体? -
因为我使用变体作为保持这两种值的通用接口。在某些情况下我需要双打,在某些情况下我需要字符串。它们存储在一个共享容器中,它们的顺序很重要,所以我不能使用 2 个单独的容器。
-
你真的需要返回一个字符串,而不仅仅是“使用”它:
template <typename F> void do_job_with_string(F f, const Variant& v) { std::visit(overloaded { [&](const double arg) { f(std::to_string(arg)); }, [&](const std::string& arg) { f(arg); } }, v);} -
请记住,创建和复制短字符串已经很便宜,所以如果您的大部分字符串都很短,那么应该没有问题。从技术上讲,您可以返回一个
variant<string, string_view>(yikes) 或返回一个包含string和string_view的类型,您可以在其中专门使用视图,知道随附的字符串可以在需要时充当后备存储(也是一种 yikes )。我很高兴错了,但我认为 C++ 中的条件生命周期没有真正干净直接的解决方案。 -
返回
variant<string, string_view>可能是一个选项。