首页 > 解决方案 > '': 无法从 'initializer list' 转换为 'std::shared_ptr' 关于使用自定义删除器创建 shared_ptr

问题描述

我为 SDL2 库方法创建了包装函子,以使用自定义删除器返回智能指针。它似乎对 unqiue_ptr (类图像)工作正常,但在构建期间返回 shared_ptr (类窗口)的类会出现以下错误:

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

这里SDL_CreateWindow返回 rawSDL_Window*并且IMG_Load返回 raw 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());
    }
};

预期结果:Window 类应该像 Image 类一样构建而没有错误

实际结果:Window 类因上述错误而失败,而 Image 类构建良好

更新:通过将 shared_ptr 创建逻辑移动到简单函数进一步缩小范围,我发现删除自定义Deleter()会删除构建错误。所以它似乎是罪魁祸首。但是我需要删除器,而且为什么使用 unique_ptr 的 Image 可以正常工作。

标签: c++shared-ptr

解决方案


我已经把你的例子缩小了一点:

#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到您的删除器类或使其成为结构,它将起作用。但是,您也可以跳过该类并直接传入删除函数,如果这是它需要做的所有工作(或者如果它有点复杂,则使用 lambda):

    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()。我建议您制作一个非成员或静态成员工厂函数来代替制作窗口。


推荐阅读