在 std 命名空间中,有一个方便的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;
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));
}
正如您所看到的,该apply1
函数根据引用的类型进行分支,因此看起来很奇怪。但如果没有这个,上面的测试将无法工作
问:apply1
是否可以在不使用分支的情况下以更优雅的方式编写?但同时,上述测试应该有效
if constexpr
在 C++23 可用之前,我没有找到避免的方法std::forward_like
,但您可以稍微简化实现:您不需要
std::forward
在else
分支中,因为您已经知道 (perfectlyforwarded) 的值类别val
。演示