c++ decltype(auto)返回类型和生存期问题

svmlkihl  于 2023-01-22  发布在  其他
关注(0)|答案(1)|浏览(112)

我只是在看一些代码与下面的粗略大纲。

#include <iostream>

template<typename T, typename F>
decltype(auto) do_something(T&& object, F f) {
    return f(std::forward<T>(object));
}

struct object {
    int x;
    int& ref() { return x; }
    int const& ref() const { return x; } 
};

struct f {
template<typename T>
decltype(auto) operator()(T&& x) {
   return x.ref();
}
};

int main(int argc, char** argv) {

    auto & x = do_something(object{}, f{});

    return 0;
}

如果我这样打电话

auto& do_something(object{}, f{);

围绕这一点的一些问题是,如果f返回一个引用,并且f通过移动语义取得object的所有权,我们是否会遇到生存期问题?
通过decltype(auto)返回的潜在问题是什么?
还是这样更好

#include <iostream>
#include <type_traits>

struct object {
    int x{};
    
    int const& ref() const noexcept { return x; }
    int& ref() noexcept { return x; }
};

struct functor {
    
    template<typename T>
    decltype(auto) operator()(T&& arg) {
        return arg.ref();
    }
};

template<typename T, typename F>
decltype(auto) apply(T&& o, F f) {
    if constexpr(std::is_lvalue_reference_v<T>) {
        return f(std::forward<T>(o)); 
    }else {
        auto result = f(std::forward<T>(o));
        return result;
    }
}
xfb7svmp

xfb7svmp1#

f通过移动语义取得对象的所有权
如果f是一个函数,那么即使没有 Package 器,它也不可能从一开始就返回对它的有效引用。
但是您的代码并不拥有它所赋予的对象的所有权,所以这是正常的。
还有一种不同的、相当模糊的失败场景:f是一个函子,它把参数移到它的成员变量中,然后返回一个对它的引用,那么在f死了之后,你会得到一个悬空引用:

struct A
{
    object x;
    object &operator()(object y)
    {
        x = std::move(y);
        return x;
    }
};

这里,A{}(object{}).foo()是法律的的,但是do_something(object{}, A{}).foo()是UB。
解决方案是使用F &&f(无论如何,这是一个好主意,以避免复制)。在调用它时,您还应该使用std::forward<F>(f)

template <typename T, typename F>
decltype(auto) do_something(T &&object, F &&f)
{
    return std::forward<F>(f)(std::forward<T>(object));
}

相关问题