我是否在这个通用的 unique_ptr<>() 删除器中正确使用了指针类?

2024-01-01

我创建了一个通用删除器模板,可用于创建unique_ptr<>()子类型允许Deleter不仅仅是delete ptr.

它与默认优化标志(即-O0),但是,当我使用-O3 the T & operator * ()函数以某种方式返回0而不是f_pointer内容。

我想确保我们同意编译器中存在问题并且我的模板是正确的。以下是一段完整的代码,应该在 Ubuntu 16.04 和 Ubuntu 18.04 以及可能的其他版本下编译,只要它们支持 C++14(请参阅下面的测试 g++ 版本)。

// RAII Generic Deleter -- allow for any type of RAII deleter
//
// To break compile with:
//     g++ --std=c++14 -O3 -DNDEBUG ~/tmp/b.cpp -o b

#include <memory>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

template<class T, T null_value, class D, D deleter>
class raii_generic_deleter
{
public:
    class pointer
    {
    private:
        T f_pointer = null_value;

    public:
        pointer(T p)
            : f_pointer(p)
        {
        }

        pointer(std::nullptr_t = nullptr)
            : f_pointer(null_value)
        {
        }

        explicit operator bool () const
        {
            return f_pointer != null_value;
        }

        bool operator == (pointer const rhs) const
        {
            return f_pointer == rhs.f_pointer;
        }

        bool operator != (pointer const rhs) const
        {
            return f_pointer != rhs.f_pointer;
        }

        T & operator * ()
        {
            return f_pointer;
        }
    };

    void operator () (pointer p)
    {
        deleter(*p);
    }
};


typedef std::unique_ptr<int,
            raii_generic_deleter<int, -1, decltype(&::close), &::close>>
                        raii_fd_t;


int main(int argc, char * argv [])
{
    int fd = -1;

    {
        raii_fd_t safe_fd;

        std::cout << "default initialization: safe_fd = " << *safe_fd
                  << std::endl;

        fd = open("/tmp/abc.tmp", O_RDWR | O_CREAT, 0700);

        std::cout << "fd = " << fd << std::endl;

        safe_fd.reset(fd);

        std::cout << "safe_fd after the reset(" << fd
                  << ") = " << *safe_fd << std::endl;
    }

    if(fd != -1)
    {
        // assuming the safe_fd worked as expected, this call returns an error
        //
        int r = close(fd);
        int e(errno);

        std::cout << "second close returned " << r
                  << " (errno = " << e << ")" << std::endl;
    }

    return 0;
}

(原文见raii_generic_deleter.h https://github.com/m2osw/snapwebsites/blob/master/libsnapwebsites/src/snapwebsites/raii_generic_deleter.h在 libsnap 网站中)

这是我使用时得到的输出-O0(无优化):

default initialization: safe_fd = -1
fd = 3
safe_fd after the reset(3) = 3
second close returned -1 (errno = 9)

在这种情况下*safe_fd来电返回-1 and 3正如预期的那样。这调用了模板T & pointer::operator * ()功能。

通过任何级别的优化(-O1, -O2, -O3)输出如下所示:

default initialization: safe_fd = 0
fd = 3
safe_fd after the reset(3) = 0
second close returned -1 (errno = 9)

正如我们所看到的,安全文件描述符返回0代替-1初始化后然后再次0当它应该是3。但是,析构函数正确关闭文件,因为第二次关闭按预期失败。换句话说,不知何故,文件描述(3) 是删除者已知并正确使用的。

当我以这种方式更新指针运算符时:

        T & operator * ()
        {
            std::cout << "f_pointer within operator * = " << f_pointer
                      << std::endl;
            return f_pointer;
        }

那么任何优化级别的输出都是正确的:

f_pointer within operator * = -1
default initialization: safe_fd = -1
fd = 3
f_pointer within operator * = 3
safe_fd after the reset(3) = 3
f_pointer within operator * = 3
second close returned -1 (errno = 9)

这可能是因为该特定功能没有得到完全优化。

编译器:

我在 Ubuntu 16.04 上使用 Stock g++ 进行了测试

g++ (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609

也在 Ubuntu 18.04 上

g++(Ubuntu 7.3.0-16ubuntu3)7.3.0

我也继续将其报告为GNU 网站上的错误 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86296.


该问题似乎是由于 libstdc++ 的实现造成的unique_ptr::operator*。这是一种非常简单、精简的方式:

struct pointer
{
    pointer(int val = -42) : z(val) { }
    int z = -42;
    int& operator*() { return z; }
};

struct my_unique_ptr
{
    pointer rep;
    pointer get() { return rep; }
#ifdef PROBLEM
    int& operator*() { return *get(); } // libstdc++ implementation
#else
    int& operator*() { return *rep; } // libc++ implementation
#endif
};

int main()
{
    my_unique_ptr q;
    std::cout << *q << "\n";
}

现在很明显 libstdc++ 不可能与您的实现一起工作pointer,因为它返回对本地临时对象的引用operator*. Any pointer存储自己的指针也会有同样的问题。

从标准角度来看,这似乎不是 libstdc++ 中的错误。该标准规定unique_ptr::operator*()回报*get(),libstdc++ 忠实地做到了这一点。

如果有的话,这是标准中的缺陷。

立即解决的方法是停止定义operator*在你的pointer class. unique_ptr不需要它(NullablePointer 不需要提供它)。

Since pointer事实上只不过是一个包装T为给定常量提供值初始化,定义一个更有意义operator T()为它,并使用get()“取消引用”相应的unique_ptr.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我是否在这个通用的 unique_ptr<>() 删除器中正确使用了指针类? 的相关文章

  • 如何在流星运行时编译新模板?

    如何使用 Handlebars js 在流星中运行时编译新模板 var source
  • 错误:表达式不可赋值三元运算符

    我有以下代码 MPLABX XC8 编译器给出此错误 错误 表达式不可分配 U1ERRIRbits RXFOIF uart1 oerr 1 uart1 oerr 0 这是相关代码部分 typedef union struct bool fe
  • 在 LINQ 查询中进行转换

    是否可以在 LINQ 查询中进行强制转换 为了编译器的缘故 下面的代码并不糟糕 但最好将其放入一个查询中 Content content dataStore RootControl as Controls Content List
  • 未找到 DEADLINE 调度策略

    我想在 C 中实现 DEADLINE 调度策略 我知道该功能已实现Linux 3 14 10我正在使用 Ubuntu 14 04Linux 3 17 0 031700 lowlatency 201410060605 SMP PREEMPT这
  • 并行运行多个任务

    我有一个代理列表 每个代理都会访问不同的站点并从站点中提取所需的数据 目前它一次只做一个 但我希望同时运行 10 20 个任务 这样它就可以一次性从 20 个站点下载 而不是只下载一个 这是我目前正在做的事情 private async T
  • 使用 POST 的 HttpWebRequest 的性能

    我有一个用于测试网络服务的小工具 它可以使用 POST 或 GET 调用 Web 服务 使用POST的代码是 public void PerformRequest WebRequest webRequest WebRequest Creat
  • 如何从经过身份验证的 SecurityToken 中获取声明

    我将令牌作为字符串传递到 SOAP 服务中 并验证了该令牌是否有效 我现在有一个 SecurityToken 在调试模式下我可以看到所有声明 特别是我想传递到另一个方法的 userId 声明 我似乎不知道如何获得这些索赔 现在 我解码了令牌
  • CMake(Ninja 后端)使用 /MT 编译

    我有一个类似的问题CMake 使用 MT 而不是 MD 进行编译 https stackoverflow com questions 14172856 cmake compile with mt instead of md但有一些差异 我正
  • 我担心我添加了太多接口

    我正在构建我的领域模型并继续重构它 正如我所做的那样 我发现我喜欢接口 因为它允许我根据接口为具体类型创建可重用的方法 控制器 视图 但是 我发现每次向域实体之一添加新属性时 我都会创建一个接口 例如 我有一个会员状态从抽象继承的对象Ent
  • C# 结构默认值

    我有一个方法 它接受一个包含许多具有基本数据类型的字段的结构 我想传递大部分默认值 但需要进行一些调整 但我了解结构声明中的基本字段不能包含默认值声明 例如struct S int a 42 现在是这样的 OptionsStruct opt
  • 维护 VS Test Project 中单元测试方法之间的上下文

    我想按顺序运行以下单元测试 使用随机数字的名称 密码等创建新客户 检索刚刚创建的客户并断言其属性包含相同的随机数 对同一用户调用 ForgotPassword 函数 并使用相同的随机数作为用户名 清楚地看到 我需要生成一次随机数 并在 3
  • 加载 QPixmap 数据的更好方法

    更好的方法来做到这一点 没有QImage QImage image width height QImage Format RGB888 memcpy image bits m frameRGB gt data 0 height width
  • 用于 C++ 中图像分析的 OpenCV 二进制图像掩模

    我正在尝试分析一些图像 这些图像的外部周围有很多噪声 但内部有一个清晰的圆形中心 中心是我感兴趣的部分 但外部噪声正在影响我对图像的二进制阈值处理 为了忽略噪音 我尝试设置一个已知中心位置和半径的圆形蒙版 从而使该圆之外的所有像素都更改为黑
  • 公交车公共交通算法

    我正在开发一个可以查找公交路线的离线 C 应用程序 我可以提取时间表 巴士 路线数据 我正在寻找适用于基本数据的最简单的解决方案 可以使用什么算法来查找从巴士站 A 到巴士站 B 的路线 是否有适用于 C Java 的开源解决方案 数据库的
  • 大量互斥体对性能的影响

    假设我有一个包含 1 000 000 个元素的数组 以及多个工作线程 每个线程都操作该数组中的数据 工作线程可能会使用新数据更新已填充的元素 但每个操作仅限于单个数组元素 并且独立于任何其他元素的值 使用单个互斥锁来保护整个数组显然会导致高
  • 如何在 C 中链接目标文件?失败并显示“架构 x86_64 的未定义符号”

    因此 我尝试在我的文件 file2 c 中使用另一个 C file1 c 文件中定义的函数 为了做到这一点 我包含了 file1 file1 h 的标头 但是 每当我尝试使用 gcc 编译文件时 我都会收到以下错误 Undefined sy
  • main.cpp 是必需的吗?

    我试图编译一个程序cmake 我最终删除了我的main cpp文件 我刚刚将其复合到另一个包含我的项目名称的文件中 即 我刚刚将主函数剪切并粘贴到该文件中 问题是我有一个main cpp未发现错误 不确定是否在C 一个名为main cpp是
  • g++ / gcc 是否支持 C++20 新的atomic_flag 功能?

    根据参考参数 https en cppreference com w cpp atomic atomic flag c 20 有丰富的 对我来说有用的 支持atomic flag运营 然而 目前尚不清楚 gcc 是否支持这些功能 它们在任何
  • Windows Phone 的 JSON 反序列化

    我正在尝试反序列化以下 JSON 但我真的不知道如何使用 JSON net 来完成这项工作 我正在使用 C 和 JSON Net 库 我的 JSON 如下 found 3 bounds 43 54919 172 62148 43 54487
  • 跟踪白色背景中的白球(Python/OpenCV)

    我在 Python 3 中使用 OpenCV 来检测白场上的白 黑球 并给出它的精确 x y 半径 和颜色 我使用函数 cv2 Canny 和 cv2 findContours 来找到它 但问题是 cv2 Canny 并不总是检测到圆的完整

随机推荐