в качестве ввода, чтобы он работал просто отлично. Время от времени удалитель может быть не идеальным.)
дал общий шаблон удаления, который можно использовать для созданияunique_ptr<>()
подтипы, позволяющиеDeleter
кроме простоdelete ptr
.
Он прекрасно работает с флагами оптимизации по умолчанию (т.е.-O0
), однако, когда я использую-O3
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 в веб-сайтах 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
а также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)
Вероятно, потому, что эта конкретная функция не оптимизирована полностью.
Составители:
Я тестировал со стоковым g ++ на Ubuntu 16.04
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.