【问题标题】:'<function-style-cast>': cannot convert from 'initializer list' to 'std::shared_ptr<SDL_Window>' on creating shared_ptr with custom deleter'<function-style-cast>': 在使用自定义删除器创建 shared_ptr 时无法从 'initializer list' 转换为 'std::shared_ptr<SDL_Window>'
【发布时间】:2019-05-09 13:59:19
【问题描述】:

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

'&lt;function-style-cast&gt;': cannot convert from 'initializer list' to 'std::shared_ptr&lt;SDL_Window&gt;'

这里的SDL_CreateWindow 返回原始SDL_Window*IMG_Load 返回原始SDL_Surface*

我尝试将Deleter 移至公共并删除 Window 类的复制限制,但仍然失败并出现同样的错误。此外,如果我只是从 Window 的函数转换中返回 nullptr,那么它构建得很好。所以问题似乎与 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


    【解决方案1】:

    我已经把你的例子精简了一点:

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

    【讨论】:

    • 谢谢!有效。我正要问你为什么它适用于 Image 类(返回 unique_ptr),但是用你的修复再次重建,这次我得到了编译器更明确的错误:``` Error C2248 'Image::Deleter::operator ( )': 无法访问在类 'Image::Deleter' SDLGame ``` 中声明的私有成员,因此它也需要 public :) 。为什么编译器没有对 shared_ptr 发出明确的警告仍然是个谜。
    • 模板错误通常会非常严重,而且很难在噪音中找到信号。我会怪罪的。 :-)
    • 另外,感谢您指出设计中的缺陷。我没有想到直接使用该功能。我将采用您建议的第一种工厂方法。
    • 不客气。我在上面的最后一个代码 sn-p 中添加了一个工厂函数(以及最后一个使用它的 Coliru 链接)。 PS,感谢您接受这个作为答案。如果你想表达更多的爱,你也可以投票给我的答案。 :-)
    • 我投了赞成票。但作为我的代表
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-17
    • 2011-04-22
    • 2020-06-19
    • 1970-01-01
    相关资源
    最近更新 更多