«<function-style-cast>»: невозможно преобразовать из «списка инициализатора» в «std :: shared_ptr <SDL_Window>» при создании shared_ptr с пользовательским средством удаления - PullRequest
0 голосов
/ 09 мая 2019

Я создал функторы-обертки для методов библиотеки SDL2, чтобы возвращать умные указатели с пользовательскими удалителями.Кажется, что это нормально работает для unqiue_ptr (класс Image), но выдает следующую ошибку для классов, возвращающих shared_ptr (класс Window) во время сборки:

'<function-style-cast>': cannot convert from 'initializer list' to 'std::shared_ptr<SDL_Window>'

SDL_CreateWindow здесь возвращает raw SDL_Window* и IMG_Load возвращает необработанное SDL_Surface*.

Я пытался переместить Deleter в общедоступный и удалить ограничения на копирование класса Window, но все равно не удается с той же ошибкой.Кроме того, если я просто верну nullptr из приведения функции Window, он будет работать нормально.Таким образом, проблема, похоже, связана с созданием самого shared_ptr.Что меня поражает, так это то, что он отлично работает с unique_ptr, но не shared_ptr.

#pragma once
#include <memory>
#include <SDL.h>
#include "Uncopyable.h"

// fails during build with error: '<function-style-cast>': 
// cannot convert from 'initializer list' to 'std::shared_ptr<SDL_Window>'
class Window:private Uncopyable {
private:


public:
    class Deleter {
        void operator()(SDL_Window *window) {
            SDL_DestroyWindow(window);
        }
    };

    static const int SCREEN_WIDTH = 800;
    static const int SCREEN_HEIGHT = 600;
    std::shared_ptr<SDL_Window> operator()() const {
        return std::shared_ptr<SDL_Window>(
            SDL_CreateWindow("SDL Tutorial",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                SCREEN_WIDTH,
                SCREEN_HEIGHT,
                SDL_WINDOW_SHOWN),
            Deleter());
    }
};

#pragma once
#include <memory>
#include <string>
#include <SDL.h>
#include <SDL_image.h>
#include "Uncopyable.h"

// builds fine
class Image: private Uncopyable {
public:
    class Deleter{
        void operator()(SDL_Surface *image) {
            SDL_FreeSurface(image);
        }
    };

    std::unique_ptr<SDL_Surface, Deleter> operator()(const std::string &path) const {
        return std::unique_ptr<SDL_Surface, Deleter>(
            IMG_Load(path.c_str()),
            Deleter());
    }
};

Ожидаемый результат: класс окна должен создаваться без ошибок, как класс изображения

Фактический результат: класс окназавершается с ошибкой, указанной выше, в то время как класс Image строится нормально

Обновление : сужая дальше, перемещая логику создания shared_ptr в простую функцию, я обнаружил, что удаление пользовательского Deleter() устраняет ошибку сборки,Так что, похоже, виновник.Но мне нужен Deleter, а также, почему тот же самый конструктор отлично работает с Image, используя unique_ptr.

1 Ответ

1 голос
/ 09 мая 2019

Я немного уменьшил ваш пример:

#include <memory>

// Stub these out since we don't have them available and they don't really matter
// for the substance of the question.
struct SDL_Window {};
void SDL_DestroyWindow( SDL_Window* win ) { delete win; }
SDL_Window* SDL_CreateWindow() { return new SDL_Window{}; }

// fails during build with error: '<function-style-cast>': 
// cannot convert from 'initializer list' to 'std::shared_ptr<SDL_Window>'
class Window {
public:
    class Deleter {
        void operator()(SDL_Window *window) {
            SDL_DestroyWindow(window);
        }
    };

    std::shared_ptr<SDL_Window> operator()() const {
        return std::shared_ptr<SDL_Window>(
            SDL_CreateWindow(),
            Deleter());
    }
};

int main()
{
    auto win = Window();
    auto sp = win();
}

Теперь проблема более очевидна:

/usr/local/include/c++/8.2.0/bits/shared_ptr_base.h:642:11: error: 
  'void Window::Deleter::operator()(SDL_Window*)' is private within this context
        __d(__p); // Call _Deleter on __p.
        ~~~^~~~~

main.cpp:16:14: note: declared private here
         void operator()(SDL_Window *window) {

См., Что она не работает в прямом эфире на Coliru .

Если вы добавите public к своему классу удаления или сделаете его структурой, он будет работать.Но вы также можете пропустить этот класс и просто передать функцию удаления напрямую, если это все, что нужно сделать (или использовать лямбду, если она немного сложнее):

    std::shared_ptr<SDL_Window> operator()() const {
        return std::shared_ptr<SDL_Window>(
            SDL_CreateWindow(),
            SDL_DestroyWindow);
    }

    // Or with a lambda if it's more complicated (here also using a factory func)
    static std::shared_ptr<SDL_Window> Create() {
        return std::shared_ptr<SDL_Window>(
            SDL_CreateWindow(),
            [] (auto win) { 
                UnregisterMyWindow( win );
                SDL_DestroyWindow( win ); 
             });
    }

Посмотрите, как это работаетжить на Coliru .

Также сомнительно мудро использовать Window::operator() как этот.Я бы посоветовал вам сделать функцию-фабрику, не являющуюся членом или статической, вместо того, чтобы создавать окна.

...