【问题标题】:No appropriate default constructor available for std::unique_ptr没有可用于 std::unique_ptr 的适当默认构造函数
【发布时间】:2018-11-30 01:56:01
【问题描述】:

这是我之前帖子的延续。由于它已经关闭,我决定发布一个新帖子。我删除了一半的代码以使其更具可读性。

我读过的一些帖子:

Smart pointers with SDL

Is it possible to use SDL2 with smart pointers?

Couple of questions about SDL_Window and unique_ptr

class cGraphics
{
public:
    //  Creator functions
    std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> Create_Window(int xWin, int yWin);

    //  ctor & dtor
    cGraphics() : m_Window(nullptr, SDL_DestroyWindow) {}
    cGraphics(int xWin, int yWin);
    ~cGraphics();
private:
    std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> m_Window;

};

cGraphics::cGraphics(int xWin, int yWin)
{
    m_Window = std::move(Create_Window(xWin, yWin));

    if (m_Window == nullptr)
    {
        throw "SDL_Window or SDL_Renderer not ready!";
    }
}

cGraphics::~cGraphics()
{
    IMG_Quit();
    SDL_Quit();
}

std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> cGraphics::Create_Window(int xWin, int yWin)
{
    return std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xWin, yWin, SDL_WINDOW_SHOWN), SDL_DestroyWindow);
}

编译器抱怨:

'std::unique_ptr<SDL_Window,void (__cdecl *)(SDL_Window *)>::unique_ptr': no appropriate default constructor available

我了解,当编译器无法找到某些成员的默认构造函数时,通常会出现此错误。然而,这不是真的,因为我明确声明了 std::unique_ptr 的默认值。

如果编译器实际上在抱怨 SDL_Window,这是一个不完整的类型(C 结构),我该怎么办?

【问题讨论】:

  • 顺便说一句,考虑手动定义您自己的删除器 (struct Invoke_SDL_DestroyWindow { void operator()(SDL_WINDOW* p) const noexcept { SDL_DestroyWindow(p); } };) 并使用它,而不是拖着其他指针并通过它们间接。
  • @Deduplicator 所说的非常正确。 std::unique_ptr 带有删除函数指针的大小是没有它的指针的两倍。这是浪费空间。你拖着一个你永远不会改变的额外指针,它也使编译器更难优化(它可能无法判断删除函数永远不会改变)。如果太痛苦(IMO 是这样),请编写一个简单的包装器,从函数指针创建一个删除器:godbolt.org/g/sM75NC(C++17,但您可以在它之前接近该语法)
  • @Justin: 或者,add an explicit template-specialization: template &lt;&gt; inline void std::default_delete&lt;SDL_Window&gt;::operator()(SDL_Window* p) const { SDL_DestroyWindow(p); } 可能更容易。 coliru.stacked-crooked.com/a/91120d518230a816
  • @Deduplicator 这是允许的边界。我强烈建议不要这样做。这很容易出错。考虑SDL_Window* 是如何由SDL_CreateWindow 创建的,而不是通过new SDL_Window; 创建的。因此,在编写该代码之后,如果编写了std::make_unique&lt;SDL_Window&gt;(...),您将有未定义的行为。简而言之,这非常容易搞砸。
  • @Justin:由于SDL_Window 不完整,对std::make_unique 的调用将导致编译错误。那里没有危险。

标签: c++ sdl unique-ptr


【解决方案1】:

std::unique_ptr&lt;SDL_Window, decltype(&amp;SDL_DestroyWindow)&gt; 不是默认可构造的。这意味着在

cGraphics::cGraphics(int xWin, int yWin) ***
{
    m_Window = std::move(Create_Window(xWin, yWin));

    if (m_Window == nullptr)
    {
        throw "SDL_Window or SDL_Renderer not ready!";
    }
}

当您到达*** 部分时,编译器将尝试默认构造m_Window,因为您没有在成员初始化列表中这样做。从编译器尝试默认构造 m_Window 是导致错误的原因。我们可以通过将m_Window = std::move(Create_Window(xWin, yWin)); 移出构造函数体并将其放入成员初始化列表中来解决此问题,如

cGraphics::cGraphics(int xWin, int yWin) : m_Window(Create_Window(xWin, yWin))
{   
    if (m_Window == nullptr)
    {
        throw "SDL_Window or SDL_Renderer not ready!";
    }
}

如果您不想这样做,那么您可以委托给默认构造函数,然后像您最初所做的那样分配给m_Window。看起来像

cGraphics::cGraphics(int xWin, int yWin) : cGraphics()
{
    m_Window = Create_Window(xWin, yWin);

    if (m_Window == nullptr)
    {
        throw "SDL_Window or SDL_Renderer not ready!";
    }
}

【讨论】:

  • 你不需要 std::move 那里 - CreateWindow 函数已经返回一个临时的。
  • 旁白:std::move 在所有这些代码中都是不必要的。
  • @SergeyA 我已经删除了这些动作,没有看到它们是多余的,谢谢。至于为什么委托工作,这是因为OP在默认构造函数中正确初始化了指向nullptr的指针。
  • @NathanOliver,确定我是在输入评论后才知道的(甚至在宽限期窗口内删除了我愚蠢的证据!):)
  • @NicholasHumphrey,事实上,初始化列表比委托构造函数更优雅。
【解决方案2】:

这是您定义 unique_ptr 的方式:

std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> m_Window;

这意味着,它的构造函数必须使用客户删除器的 instance 调用,在您的情况下,函数 SDL_DestroyWindow - 请记住,您告诉指针什么是删除器的类型,而不是实际删除器(在您的情况下为函数指针)。

要使这段代码正常工作,您必须使用删除器实例正确构造指针,例如:

cGraphics::cGraphics(int xWin, int yWin) : 
                     m_Window{Create_Window(xWin, yWin)} {...}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-15
    • 2012-11-24
    相关资源
    最近更新 更多