考虑以下问题:我有许多类,每个类都实现一个get()
函数。以下container1
和container2
是此类的示例:
struct expensive_type {
int v;
};
struct container1 {
expensive_type get() const {
return { 1 };
}
};
struct container2 {
expensive_type x;
expensive_type& get() {
return x;
}
};
我想创建一个以C
和F
为模板的包装器,该包装器实现与get()
相同的C
功能,但将一个功能应用于结果:
template<typename C, typename F>
struct wrapper {
F f;
C c;
decltype(auto) get() {
return f(c.get());
}
};
我现在想为一个简单的包装器创建一个函数f
,该函数只返回其参数不变。我认为这可以工作:
auto f = [](auto&& x) -> decltype(auto) {
return forward<decltype(x)>(x);
};
wrapper<container1, decltype(f)> trivial_wrapper1 { f, {} };
wrapper<container2, decltype(f)> trivial_wrapper2 { f, {} };
,但不幸的是trivial_wrapper1.get()
返回expensive_type{0}
而不是expensive_type{1}
(至少带有-O2
标志)。我想这个问题与悬挂引用有关,但是我不知道该如何解决。
我的问题是:如何正确实现函数f
以使其充当完美的身份,而无需复制其参数?
为了澄清,这是预期行为的示例:
cout << trivial_wrapper1.get().v << endl; // should print 1, prints 0 as of now
trivial_wrapper2.get().v = 2;
cout << trivial_wrapper2.c.x.v << endl; // should print 2, and it does as of now
问题是该值被转换为r值引用。当转发into函数时,这通常很好,但是您尝试转发out返回值。
代替auto&&
类型演绎,您需要decltype(auto)
类型演绎。
但是实际上,您将decltype(auto)
用作除以下以外的所有位置:auto&&
lambda参数。
我的解决方案是在get()
按值返回时衰减副本。
template<typename C, typename F>
struct wrapper {
F f;
C c;
decltype(auto) get() {
using get_t = decltype(c.get());
using f_result_t = decltype(f(c.get()));
if constexpr (std::is_reference_v<get_t>) {
return f(c.get());
} else {
return std::decay_t<f_result_t>(f(c.get())); // decay copy
}
}
};
解决方案不明显。当您这样做时会发生什么?
auto f = [](auto&& x) -> decltype(auto) {
static auto v = std::decay_t<decltype(x)>{};
return (v); // return by ref
};
wrapper<container1, decltype(f)> trivial_wrapper1 { f, {} };
wrapper<container2, decltype(f)> trivial_wrapper2 { f, {} };
现在,即使没有衰减副本,它也将按预期工作。如果使用衰减复制,则必须编写函数的意图以及其行为方式。
这里的问题是没有通过函数的临时扩展。当你做
return f(c.get());
当c
是container1
时,您按值返回,因此具有临时权限。该临时变量只存在于完整表达式的末尾,这意味着一旦return语句完成,它就会死亡。这就是为什么您有悬挂的参考。
尽管这会使您陷入困境。 f
需要做的是,如果有一个临时值,则按值返回,但是这样做可能会很昂贵。但是,如果要通过中间函数传递返回值,则实际上没有解决办法。那会给你一个f
像
auto f = [](auto&& x) -> std::conditional_t<std::is_rvalue_reference_v<decltype(x)>,
std::remove_reference_t<decltype(x)>,
decltype(x)> {
return x;
};
[对于右值,按值返回,对于左值,按引用返回。