我有以下代碼:
#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(),它接受一個變體并將其內部值轉換為字串。
變體可以包含 astd::string或 a double。
在 a 的情況下std::string,我只回傳它。
在 a 的情況下double,我std::string從 中創建 adouble然后回傳它。
問題是,我不喜歡在std::string字串變體的情況下回傳 的副本這一事實。理想情況下,我會回傳一種std::string_view或另一種字串觀察者。
但是,我無法回傳 astd::string_view因為在雙變體的情況下我需要創建一個新的臨時物件std::string并且std::string_view是非擁有的。
std::string&出于同樣的原因,我無法回傳 a 。
我想知道是否有一種方法可以優化代碼,以便在字串變體的情況下避免復制。
請注意,在我的實際用例中,我經常從字串變體中獲取字串,但很少從雙變體中獲取字串。
但我仍然希望能夠std::string從雙變體中獲得一個。
另外,在我的實際用例中,我通常只是觀察字串,所以我并不是每次都真正需要副本。std::string_view或者其他一些字串觀察器在這種情況下是完美的,但由于上述原因,這是不可能的。
我考慮了幾種可能的解決方案,但我不喜歡其中任何一個:
回傳 a
char*而不是 astd::string并在 a 的情況下在堆上的某處分配 c 字串double。在這種情況下,我還需要將整個內容包裝在一個擁有堆分配字串的類中,以避免記憶體泄漏。回傳
std::unique_ptr<std::string>帶有自定義洗掉器的 a ,它將清理堆分配的字串,但如果字串駐留在變體中,則不執行任何操作。不確定如何實作這個自定義洗掉器。更改變體,使其擁有 a
std::shared_ptr<std::string>代替。然后,當我需要來自 string-variant 的 string 時,我只回傳 shared_ptr 的副本,當我需要來自 double-variant 的 string 時,我呼叫std::make_shared().
第三種解決方案有一個固有的問題:std::string不再駐留在變體中,這意味著追逐指標并失去性能。
Can you propose any other solutions to this problem? Something which performs better than copying a std::string every time I call the function.
uj5u.com熱心網友回復:
您可以回傳一個代理物件。(這就像你的unique_ptr方法)
struct view_as_string{
view_as_string(const std::variant<double, std::string>& v){
auto s = std::get_if<std::string>(&v);
if(s){
string_ref = s;
}
else{
string_temp = std::to_string(std::get<double>(v));
string_ref = &string_temp;
}
}
const std::string& data(){return *string_ref;}
const std::string* string_ref;
std::string string_temp;
};
利用
int main()
{
std::variant<double, std::string> v1 {"Hello"};
std::variant<double, std::string> v2 {1.23};
std::cout << view_as_string(v1).data() << '\n';
std::cout << view_as_string(v2).data() << '\n';
return 0;
}
uj5u.com熱心網友回復:
問題是,一個變體包含不同的型別,但您正試圖找到一種方法來用單一型別表示所有這些型別。字串表示對于通用日志記錄很有用,但它有你描述的缺點。
對于變體,我不喜歡嘗試將這些值合并回一個單一的共同事物,因為如果這很容易實作,那么首先就不需要變體。
我認為更好的是盡可能延遲轉換,并繼續將其轉發到其他使用該值的函式,或者轉換并轉發直到它被使用——而不是嘗試提取單個值并嘗試使用它。
一個相當通用的函式可能如下所示:
template <typename Variant, typename Handler>
auto with_string_view(Variant const & variant, Handler && handler) {
return std::visit(overloaded{
[&](auto const & obj) {
using std::to_string;
return handler(to_string(obj));
},
[&](std::string const & str) {return handler(str); },
[&](std::string_view str) { return handler(str); },
[&](char const * str) { return handler(str); }
}, variant);
}
由于在通用版本中創建的臨時檔案比對處理程式的呼叫壽命更長,因此這是安全有效的。它還顯示了我發現對變體非常有用的“轉發”技術(并且通常訪問,即使對于非變體也是如此。)
此外,我沒有顯式轉換為 string_view,但該函式可以添加處理程式接受字串視圖的要求(如果這有助于記錄用法。)
使用上述輔助函式,您可以像這樣使用它:
using V = std::variant<std::string, double>;
V v1{4.567};
V v2{"foo"};
auto print = [](std::string_view sv) { std::cout << sv << "\n";};
with_string_view(v1, print);
with_string_view(v2, print);
這是一個完整的現場示例,也擴展了一點:https : //godbolt.org/z/n7KhEW7vY
uj5u.com熱心網友回復:
如果執行緒安全不是問題,您可以std::string在回傳雙精度值時簡單地使用靜態作為后備存盤。然后你就可以回傳 a std::string_view,例如:
std::string_view string_from(const Variant& v)
{
static std::string buffer;
return std::visit(overloaded {
[&buffer](const double arg) -> std::string_view { buffer = std::to_string(arg); return buffer; },
[](const std::string& arg) -> std::string_view { return arg; },
}, v);
}
在線演示
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/382304.html
