我是否在此通用unique_ptr<>()删除程序中正确使用了指针类? [英] Am I using the pointer class properly in this generic unique_ptr<>() deleter?

查看:78
本文介绍了我是否在此通用unique_ptr<>()删除程序中正确使用了指针类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个通用的删除器模板,该模板可用于创建unique_ptr<>()子类型,以允许除delete ptr之外的Deleter.

I created a generic deleter template that can be used to create unique_ptr<>() sub-types allowing for a Deleter other than just delete ptr.

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

It works great with the default optimization flags (i.e. -O0), however, when I use -O3 the T & operator * () function, somehow, returns 0 instead of the f_pointer contents.

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

I would like to make sure that we agree that there is something wrong in the compiler and that my template is correct. The following is a complete piece of code that should compile as is under Ubuntu 16.04 and Ubuntu 18.04 and probably other versions as long as they support C++14 (see below for tested g++ versions).

// 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 在libsnap网站中)

(For original, see raii_generic_deleter.h in libsnapwebsites)

使用-O0时,我会得到输出(无优化):

There is the output I'm getting when I use -O0 (no optimizations):

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

在这种情况下,*safe_fd调用将按预期返回-13.这将调用模板T & pointer::operator * ()函数.

In this case the *safe_fd call returns -1 and 3 as expected. This calls the template T & pointer::operator * () function.

使用任何级别的优化(-O1-O2-O3),输出都将如下所示:

With any level of optimization (-O1, -O2, -O3) the output looks like this:

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

如我们所见,安全文件描述符在初始化后返回0而不是-1,然后在应为3时再次返回0.但是,析构函数会正确关闭文件,因为第二次关闭会按预期失败.换句话说,文件描述(3)是已知的,并且已被删除程序正确使用.

As we can see, the safe file descriptor returns 0 instead of -1 after initialization and then again 0 when it should then be 3. However, the destructor properly closes the file since the second close fails as expected. In other words, somehow, the file description (3) is known and properly used by the deleter.

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

When I update the pointer operator in this way:

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

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

Then the output with any level of optimization is correct:

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)

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

Which is probably because that specific function doesn't get optimized out completely.

编译器:

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

I tested with stock g++ on Ubuntu 16.04

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

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

在Ubuntu 18.04上也是如此

And also on Ubuntu 18.04

g ++(Ubuntu 7.3.0-16ubuntu3)7.3.0

g++ (Ubuntu 7.3.0-16ubuntu3) 7.3.0

我也继续将其报告为GNU网站上的错误.

I also went ahead and reported this as a bug on the GNU website.

推荐答案

该问题似乎是由于unique_ptr::operator*的libstdc ++实现引起的.这是一种非常简化的简化方式:

The issue seems to be due to libstdc++ implementation of unique_ptr::operator*. Here it is in a very simplified, pared-down way:

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*中的本地临时对象的引用.任何存储自己的pointe的pointer都会遇到相同的问题.

Now it is abundantly clear that libstdc++ cannot possibly work with your implementation of pointer, because it returns a reference to a local temporary object from operator*. Any pointer that stores its own pointee will have the same issue.

就标准而言,这似乎不是libstdc ++中的错误.该标准指定unique_ptr::operator*()返回*get(),libstdc ++会忠实地执行*get().

Standard-wise, this doesn't seem to be a bug in libstdc++. The standard specifies that unique_ptr::operator*() returns *get(), which libstdc++ faithfully does.

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

If anything, this is a defect in the standard.

一个直接的解决方法是停止在您的pointer类中定义operator*. unique_ptr不需要它(不需要NullablePointer来提供它).

An immediate fix is to stop defining operator* in your pointer class. unique_ptr doesn't need it (NullablePointer is not required to provide it).

由于pointer实际上只是T的包装器,该包装器可为给定常数提供值初始化,因此为它定义operator T()并在其中使用get()会更有意义.取消引用"相应的unique_ptr.

Since pointer is in fact nothing more than a wrapper around T that provides value-initialisation to a given constant, it would make more sense to define an operator T() for it, and use get() to "dereference" the corresponding unique_ptr.

这篇关于我是否在此通用unique_ptr&lt;&gt;()删除程序中正确使用了指针类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆