I have a function which takes a generic argument that can either be a std::optional<T>
or T
directly. For the further context of the function I don't care if the argument came in as an optional, I want to just be able to use a reference to the underlying value T&
, which in case of the optional would mean I would have to conditionally extract the value using std::optional::value()
. Can this be done generically? I thought I might be able to use an std::reference_wrapper
to convert a constexpr conditional back to a reference, but it doesn't work. This is what I've got:
Demo:
#include <cstdio>
#include <optional>
#include <utility>
#include <iostream>
template <typename, template <typename...> class>
struct is_specialization_of : std::false_type {};
template <template <typename...> class Template, typename... Args >
struct is_specialization_of<Template<Args...>, Template> : std::true_type {};
template <typename T, template <typename...> class Template>
concept specialization_of = is_specialization_of<T, Template>::value;
int main() {
std::optional<int> myopt = 2;
const auto& ref = [&]{
if constexpr (specialization_of<decltype(myopt), std::optional>) {
return std::cref(myopt.value());
} else {
return std::cref(myopt);
}
}();
std::cout << ref << std::cout;
}
Yields:
<source>:27:22: error: no match for 'operator<<'
... of sorts and more. Why do I want this? Because I don't want to adapt every passage in the code to either adapt to T
's or std::optional<T>
's interface because the verbosity of that would be rampant...
Note: I also tried the non-autodeduced reference: const int& ref = ...
but with the same result.
No need to use std::reference_wrapper
here, simply declare your lambda to return a reference:
const auto& ref = [&]() -> auto& {
if constexpr (specialization_of<decltype(myopt), std::optional>) {
return myopt.value();
} else {
return myopt;
}
}();
Furthermore I would advise you to not use a fancy specialization_of
template in favor of simple function overloading:
template <typename T>
T const& extract_value(T const& t) { return t; }
template <typename T>
T const& extract_value(std::optional<T> const& t) {
return extract_value(t.value()); // Recurse to handle the case where T is std::optional<U>
}
int main() {
std::optional<int> myopt = 2;
const auto& ref = extract_value(myopt);
std::cout << ref << std::endl;
}