You could just use std::string
Firstly, a std::string could be used in a Token just as well as a std::string_view. This might not be as costly as you think, because std::string in all C++ standard libraries has SSOs (small string optimizations).
This means that short tokens like "const" wouldn't be allocated on the heap; the characters would be stored directly inside the container. Before bothering with std::string_view and std::variant, you might want to measure whether allocations are even being a performance issue. Otherwise, this is a case of premature optimization.
If you insist on std::variant ...
User @Homer512 has provided a solid solution already. Rather than using the std::variant directly, you could create a wrapper around it which provides a string-like interface for both std::string and std::string_view.
This is easy to do, because the name and meaning of most member functions is identical for both classes. That also makes them easy to use through std::visit.
struct MaybeOwningString
{
using variant_type = std::variant<std::string, std::string_view>;
using size_type = std::string_view::size_type;
variant_type v;
// main member function which grants access to either alternative as a view
std::string_view view() const noexcept {
return std::visit([](const auto& str) -> std::string_view {
return str;
}, v);
}
// various helper functions which expose commonly used member functions
bool empty() const noexcept {
// helper functions can be implemented with std::visit, but this is verbose
return std::visit([](const auto& str) {
return str.empty();
}, v);
}
size_type size() const noexcept {
// helper functions can also be implemented by using view()
return view().size();
}
// ...
};
std::stringandstd::string_viewhave similar interface, it seems thatstd::visitcode would be simple...std::variant<std::string, std::string_view>is causing you trouble. Assuming the owning/non-owning combination works out for your use case, I don't see why this should result in particularly convoluted code. Of course working withstd::variantis far from being as nice as in languages with proper sum types.std::string_viewhere worth the pain, in terms of performance (and maybe memory overhead, as well as, effectively, risk of dangling pointers)? Maybe SSO (short string optimisation) will come to your aid.std::string_viewand anstd::string. The string_view can point to the original source text or the Token's ownstd::string; the ` std::string` in the Token is empty if unnecessary.