c++c++20structured-bindings

How to correctly and elegant implement the apply function for structured binding?


In the std namespace, there is a convenient std::apply function that allows you to perform an action on each element of the tuple. Using structured binding, we can achieve the same behavior for non-tuple types. Suppose we consider a type with only one non-static data member. Therefore, I wrote the following code:

#include <iostream>
#include <functional>
#include <utility>
#include <type_traits>

template<class F, class T>
decltype(auto) apply1(F&& func, T&& val){
    if constexpr(std::is_lvalue_reference<decltype(val)>{}){
        auto& [m1] = val;
        return std::invoke(std::forward<F>(func), m1);
    }   else {
        auto&& [m1] = std::forward<T>(val);
        return std::invoke(std::forward<F>(func), std::forward<decltype(m1)>(m1));
    }
}
//tests
struct obj{int v;};
struct data{obj o;};
void ref(obj&){std::cout<<"&\n";}
void cref(const obj&){std::cout<<"const&\n";}
void temp(obj&&){std::cout<<"&&\n";}

int main(){
    data d{};
    apply1(ref, d);
    apply1(cref, d);
   //SHOULD NOT COMPILE apply1(temp, d);
    apply1(temp, std::move(d));
}

As you can see, the apply1 function has branching depending on the type of reference and therefore looks strange. But without this, the above tests will not work

Q: Is it possible to write the apply1 in a more elegant way without using branching? but at the same time, the above tests should work


Solution

  • I don't see a way to avoid if constexpr before C++23's std::forward_like becomes available, but you could slightly simplify the implementation:

    template<class F, class T>
    decltype(auto) apply1(F&& func, T&& val) {
        auto& [m1] = val;
        if constexpr (std::is_lvalue_reference_v<T&&>)
            return std::invoke(std::forward<F>(func), m1);
        else
            return std::invoke(std::forward<F>(func), std::move(m1));
    }
    

    You don't need std::forward in the else branch because you already know the value category of (perfectly forwarded) val.

    Demo