微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使将函数包装在noexcept可检测的可调用对象中的类模板用作std :: unique_ptr自定义删除器?

如何解决如何使将函数包装在noexcept可检测的可调用对象中的类模板用作std :: unique_ptr自定义删除器?

是否可以使用对象类型和自由函数作为为std::unique_ptr创建自定义删除器的参数?

我是模板的新手,来到这里:

#include <memory>

template<typename T,typename FreeFunc> struct ObjectDeleter {
  const FreeFunc &free_func;
  ObjectDeleter(const FreeFunc &free_func) : free_func(free_func){
      
  }
  void operator()(T *item) 
  {
    if (item) {
      free_func(item);
    }
  }
};
struct Foo{};
void internal_foo_free(Foo *){}
struct Bar{};
void internal_bar_free(Bar *){}

using unique_foo_ptr = 
         std::unique_ptr<Foo,ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
int main(){
    return 0;
}

错误

<source>:19:48: error: wrong number of template arguments (1,should be 2)
   19 |          std::unique_ptr<Foo,ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
      |                                                ^
<source>:3:48: note: provided for 'template<class T,class FreeFunc> struct ObjectDeleter'
    3 | template<typename T,typename FreeFunc> struct ObjectDeleter {
      |                                                ^~~~~~~~~~~~~
<source>:19:50: error: lambda-expression in template-argument only available with '-std=c++2a' or '-std=gnu++2a'
   19 |          std::unique_ptr<Foo,ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
      |                                                  ^
<source>:19:87: error: template argument 2 is invalid
   19 |          std::unique_ptr<Foo,ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
      |                                                                                       ^
Compiler returned: 1

建议我使用函数指针(并且还将其扩展到std :: function):

但这增加了通过函数指针(或std :: function)添加抛出语句的可能性,编译器或静态分析器将无法检测到该指针。使用lambda进行参数设置将确保没有人可以在std :: unique_ptr的析构函数添加throwing语句。这就是我所说的“ noexcept可检测的可调用对象”

我正在使用C ++ 17。

解决方法

这是在抱怨,因为不能将lambda用作模板参数(无论如何在C ++ 20之前)。否则,lambda是可以被 调用的对象,除非函数体抛出,否则它们不会抛出,不需要包装类。您只需要这样做就很尴尬:

auto myDeleter = [&x](Foo* v) { internal_foo_free(v); };
std::unique_ptr<Foo,decltype(myDeleter)> guard { create_foo(),myDeleter };

最初,我将此问题解释为“如果有人使用未标记为noexcept的自定义删除器,我希望编译失败”。我问了一下,我想这就是为什么您编辑标题以包含该措辞的原因。

noexcept是用于优化提示/文档的限定符。它完全在荣誉系统上。您可以直接将它们放入其中,并且您的源仍将编译(尽管静态分析器可能会抱怨)。如果要强制执行自定义删除器仅调用noexcept函数,则在C ++ 17中,可以使用noexcept运算符使用静态断言,如果表达式调用non-noexcept函数则返回false :

template <auto F>
struct NoExceptDeleter{
    template <typename T>
    void operator ()(T* arg) const noexcept {
        static_assert(noexcept(F(arg)),"deleter must be marked noexcept");
        F(arg);
    }
};

void good_delete(foo* v) noexcept { free(v); }
std::unique_ptr<foo,NoExceptDeleter<good_delete>> good_guard { make_foo() };

void bad_delete(foo* v) { throw 0; }
std::unique_ptr<foo,NoExceptDeleter<bad_delete>> bad_guard { make_foo() }; // compile-error

因为这需要函数指针,所以在C ++ 17中,您只能将其与+运算符衰减为函数指针的非捕获lambda一起使用:

auto good_lambda = [](foo* v) noexcept { free(v); }
std::unique_ptr<foo,NoExceptDeleter<+good_lambda>> lambda_guard;

演示:https://godbolt.org/z/vdEov3

如果您需要在lambda中进行捕获,则必须使用有状态的删除器:

template <typename F>
struct StatefulNoExceptDeleter {
    F f;
    StatefulNoExceptDeleter(F f) : f(f) { }

    template <typename T>
    void operator ()(T* arg) const noexcept {
        static_assert(noexcept(f(arg)),"deleter must be marked noexcept");
        f(arg);
    }
};

/* ... */

int baz;
auto deleter = [&](Foo* t) noexcept {
    std::cout << "labmda: " << baz << std::endl;
    delete t;
};
std::unique_ptr<Foo,StatefulNoExceptDeleter<decltype(deleter)>> guard { 
    new Foo,StatefulNoExceptDeleter<decltype(deleter)>{ deleter }
};

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。