Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind reference to T& in both cases, std::optional<T> and T, at compile time

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.

like image 354
glades Avatar asked Nov 01 '25 16:11

glades


1 Answers

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;
}
like image 130
chrysante Avatar answered Nov 03 '25 07:11

chrysante



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!