草庐IT

c++ - 具有类型删除析构函数的 unique_ptr 不太有效(有警告)

coder 2024-02-23 原文

有一个不错的小技巧here允许使用 std::unique_ptr不完整的类型。

相关代码如下:

// File: erasedptr.h
#include <memory>
#include <functional>

// type erased deletor (an implementation type using "veneer")
template <typename T>
struct ErasedDeleter : std::function<void(T*)>
{
    ErasedDeleter()
        : std::function<void(T*)>( [](T * p) {delete p;} )
    {}
};

// A unique_ptr typedef
template <typename T>
using ErasedPtr = std::unique_ptr<T, ErasedDeleter<T>>;


// Declare stuff with an incomplete type
struct Foo;
ErasedPtr<Foo> makeFoo();


// File: main.cpp (Foo's definition is not available in this translation unit)
#include "erasedptr.h"
int main() {
    ErasedPtr<Foo> f;  // [R1]
    f = makeFoo();
    // ~Foo() gets called fine
}

// File: foo.cpp
#include <iostream>
#include "erasedptr.h"
struct Foo {
    ~Foo() { std::cout << "~Foo()\n" ; }
};
ErasedPtr<Foo> makeFoo() { return ErasedPtr<Foo>(new Foo); }

这适用于我尝试过的所有编译器:gcc 4.9、clang 3.5 以及 msvc VS13 和 VS15。但它们都会产生以下警告:

 deletion of pointer to incomplete type 'Foo'; no destructor called

如果上面的 [R1] 被替换为 ErasedPtr<Foo> f( makeFoo() ); ,警告不会出现。

最后,析构函数确实 被调用并且似乎没有实际问题。该警告是有问题的,因为在质量关键环境中不能忽略它,并且这种非常有用的模式不可用。

要复制,请创建 3 个文件 erasedptr.hpp , main.cpp , foo.cpp如上所述并编译。

所以问题是:这是怎么回事?是否可以有任何替代实现来规避此警告?

最佳答案

你的问题是有效的,但你尝试使用的代码有点复杂,所以让我停下来找到想要的解决方案。

  • 您在这里所做的不是类型删除(Andrzej 对此也错了)- 您只是将删除捕获到运行时函数值中,顺便说一句,没有任何好处。类型删除 OTH 是指其他代码部分丢失有关初始类型的信息。
  • 我在 VS 2015 上试过你的代码,它也调用析构函数 ~Foo(),但这只是运气好,这意味着编译器正在做一些奇特的事情。如果您不使用 std::function 而是编写自己的自定义删除器,则不会调用析构函数。

方案一——类型删除

如果您真的想删除类型,您可以按以下方式编写/使用 ErasedPtr:

erasedptr.h

// file erasedptr.h

#include <memory>
#include <functional>

// make type erased deleter
template <typename T>
std::function<void(void*)> makeErasedDeleter()
{
    return {
        [](void* p) {
            delete static_cast<T*>(p);
        }
    };
};

// A unique_ptr typedef
template <typename T>
using ErasedPtr = std::unique_ptr<T, std::function<void(void*)>>;

foo.cpp

// file foo.cpp

#include <iostream>
#include "erasedptr.h."

struct Foo {
    ~Foo() { std::cout << "~Foo()\n" ; }
};

// capture creation and deletion of Foo in this translation unit
ErasedPtr<Foo> makeFoo() {
    return { new Foo, makeErasedDeleter<Foo>() };
}

main.cpp

// file main.cpp (Foo's definition is not available in this translation unit)

#include "erasedptr.h"

// fwd decl Foo
struct Foo;
ErasedPtr<Foo> makeFoo();

int main() {
    ErasedPtr<Foo> f;  // [R1]
    f = makeFoo();
    // ~Foo() gets called fine
}

这样只有 foo.cpp 需要知道实际类型并将删除捕获到 std::function 中。

解决方案 2 - 不完整的类型,真的

您真正想要的是处理不完整的类型。 STL 的默认删除器 std::default_delete 存在的“问题”是它在编译时断言删除是否安全——这该死的是对的!

要使其正常工作,就是使用显式模板实例化告诉编译器/链接器您实际上关心删除的正确实现。这样你就不需要任何特殊的 typedef/template 别名作为你的唯一指针:

foo.cpp

// file foo.cpp

#include "foo_fwddecl.h"

// capture creation of Foo in this translation unit
std::unique_ptr<Foo> makeFoo() {
    return std::make_unique<Foo>();
}

// explicitly instantiate deletion of Foo in this translation unit
template void std::default_delete<Foo>::operator()(Foo*) const noexcept;
template void std::default_delete<const Foo>::operator()(const Foo*) const noexcept;
// note: possibly instantiate for volatile/const volatile modifiers

foo_fwddecl.h

#include <memory>

struct Foo;

std::unique_ptr<Foo> makeFoo();

extern template void std::default_delete<Foo>::operator()(Foo*) const noexcept;
extern template void std::default_delete<const Foo>::operator()(const Foo*) const noexcept;
// note: possibly instantiate for volatile/const volatile modifiers

main.cpp

// file main.cpp (Foo's definition is not available in this translation unit)

#include "foo_fwddecl.h"

int main() {
    std::unique_ptr<Foo> f;  // [R1]
    f = makeFoo();
    // ~Foo() gets called fine
}

关于c++ - 具有类型删除析构函数的 unique_ptr 不太有效(有警告),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35929654/

有关c++ - 具有类型删除析构函数的 unique_ptr 不太有效(有警告)的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  3. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  9. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  10. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

随机推荐