std::apply
函数,允许您对元组的每个元素执行操作。使用结构化绑定,我们可以为非元组类型实现相同的行为。假设我们考虑一种只有一个非静态数据成员的类型。因此,我编写了以下代码:
#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));
}
}
//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));
}
如您所见,apply1
函数根据引用类型进行分支,因此看起来很奇怪。但如果没有这个,上面的测试将无法工作问: 是否可以在不使用分支的情况下以更优雅的方式编写
apply1
?但同时,上述测试应该有效
在 C++23 的
if constexpr
可用之前,我没有找到避免
std::forward_like
的方法,但您可以稍微简化实现: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));
}
您不需要 std::forward
分支中的 else
,因为您已经知道
val
的值类别。