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;
std::invoke(std::forward<F>(func), m1);
} else {
auto&& [m1] = std::forward<T>(val);
std::invoke(std::forward<F>(func), std::forward<decltype(m1)>(m1));
}
}
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.
Q: Is it possible to write the apply1
function in a more elegant way without using branching?