Unique_ptr和默认的可构造指针

unique_ptr and default constructible pointer

本文关键字:指针 默认 ptr Unique      更新时间:2023-10-16

最近我尝试通过std::unique_ptr重新定义作用域保护 (注意: Deleter具有成员类型pedef pointer -是std::unique_ptr的特殊处理情况):

#include <type_traits>
#include <utility>
#include <memory>
#include <iostream>
#include <cstdlib>
#include <cassert>
namespace
{
template< typename lambda >
auto
make_scope_guard(lambda && _lambda)
{
    struct lambda_caller
    {
        using pointer = std::decay_t< lambda >;
        void
        operator () (lambda & l) const noexcept
        {
            std::forward< lambda >(l)();
        }
    };
    return std::unique_ptr< std::decay_t< lambda >, lambda_caller >(std::forward< lambda >(_lambda));
}
}
int
main()
{
    std::cout << 1 << std::endl;
    {
        std::cout << 2 << std::endl;
        [[gnu::unused]] auto && guard_ = make_scope_guard([&] { std::cout << __PRETTY_FUNCTION__ << std::endl; });
        std::cout << 3 << std::endl;
    }
    std::cout << 5 << std::endl;
    return EXIT_SUCCESS;
}

对于传递给make_scope_guard的自由函数void f() { std::cout << 4 << std::endl; }的简单指针,这种方法可以很好地工作,但对于传递给make_scope_guard的lambda则不行。

这是由于std::unique_ptr定义中有大量的... = pointer()(函数默认参数,默认数据成员等),但我无法在本文中找到pointerDefaultConstructible要求。

是强制性的,pointer应该匹配std::is_default_constructible的要求?

它对libc++libstdc++进行了测试,使用不太旧的clang++ -std=gnu++1z

似乎,应该有lambdas的语言扩展:如果auto l = [/* possible capture list */] (Args...) { /* code */; };然后using L = decltype(l);相当于struct L { constexpr void operator () (Args...) const noexcept { ; } };的一些Args...,不是吗?

附加:

make_scope_guard(D{})提供以下DefaultConstructible类的实例D{},要求在if (p) { ...上下文中取消注释掉的代码,其中p的类型为D:

struct D { void operator () () const noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; } /* constexpr operator bool () const { return true; } */ };

unique_ptr仍然是一个指针。你不能把一个塞进去。从[unique.ptr]:

唯一指针是拥有另一个对象并通过指针管理该对象的对象。更准确地说,唯一指针是一个对象u,它存储指向第二个对象p的指针,并将其释放P

[…]此外,u可以根据请求将所有权转移给另一个惟一的指针u2。完成后这样的转移,下列后置条件成立:[…]] u。p等于nullptr

lambda不是指针。一个lambda不能等于nullptr

也就是说,您已经创建了自己的局部结构,为什么不使用做RAII作用域保护自己,而不是推迟unique_ptr ?这看起来充其量只是一个hack,并且需要更多的代码来启动。你可以这样写:
template< typename lambda >
auto
make_scope_guard(lambda && _lambda)
{
    struct lambda_caller
    {
        lambda _lambda;
        ~lambda_caller()
        {
            _lambda();
        }
    };
    return lambda_caller{std::forward<lambda>(_lambda)};
}

如果你需要支持release,你可以把_lambda包装在boost::optional里面,这样lambda_caller就变成:

struct lambda_caller
{
    boost::optional<lambda> _lambda;
    ~lambda_caller()
    {
        if (_lambda) {
            (*_lambda)();
            _lambda = boost::none;
        }
    }
    void release() {
        _lambda = boost::none;
    }
};