Search code examples
c++unique-ptr

C2661 when using std::make_unique


I have an error when I am trying to use std::make_unique. I probably have made a very simple mistake and I would be thankful if someone pointed it out. I am using GLAD and GLFW as 3rd party libraries. I get an error code of C2661 when I run it, C2661 'Window::Window': no overloaded function takes 3 arguments on the next code snippet in window.cpp.

return std::make_unique<Window>(std::move(nativeWindow), width, height);
Window.hpp
#pragma once

#include <memory>
#include <string>

#include <GLFW/glfw3.h>

struct Window final {
public:
    std::unique_ptr<Window> createWindow(int width, int height, std::string title);

    bool shouldClose() { return glfwWindowShouldClose(nativeWindow); }

    void swapBuffers() { glfwSwapBuffers(nativeWindow); }
private:
    GLFWwindow* nativeWindow;
    int width;
    int height;
    std::string title;
};

Window.cpp
#include "Window.hpp"

std::unique_ptr<Window> Window::createWindow(int width, int height, std::string title) {
    nativeWindow = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
    glfwMakeContextCurrent(nativeWindow);

    return std::make_unique<Window>(std::move(nativeWindow), width, height);
}

sorry for bad code quality.


Solution

  • Your Window class does not have a constructor taking GLFWwindow*, int, int as would be required for this:

    return std::make_unique<Window>(std::move(nativeWindow), width, height);
    

    However, it seems you are encapsulating the wrong thing.

    • If a Window instance is copied or moved, there will suddenly be two owners of the GLFWwindow, with a double free later likely.
    • When a Window is destroyed, you don't call glfwDestroyWindow to free the resource, which will leak.

    Read The rule of 3/5/0.

    What you should be doing is really to store a unique_ptr<GLFWwindow> in Window to make the problems mentioned above go away without having to implement the manual resource management mentioned in The rule of 3/5/0.

    Example:

    struct Window final {
    public:
        Window(int width, int height, std::string title);
    
        inline bool shouldClose() { return glfwWindowShouldClose(nativeWindow.get()); }
        inline void swapBuffers() { glfwSwapBuffers(nativeWindow.get()); }
    
    private:
        struct glfwWindowDestroyer {
            void operator()(GLFWwindow* win) const {
                glfwDestroyWindow(win);
            }
        };
        std::unique_ptr<GLFWwindow, glfwWindowDestroyer> nativeWindow;
        int width;
        int height;
        std::string title;
    };
    

    The constructor implementation would then be:

    Window::Window(int width, int height, std::string title) :
        nativeWindow(glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr)),
        width(width),
        height(height),
        title(std::move(title))
    {
        glfwMakeContextCurrent(nativeWindow.get());
    }
    

    That way the createWindow function (which should have been static) can be removed. Just create Windows like normal C++ objects. Window objects are now safely moveable (and non-copyable) without any extra tinkering.


    If you for some reason still would like to have a helper function to create unique_ptr<Window>s, you can, but as you will see, it will be rather pointless:

    std::unique_ptr<Window>
    Window::createWindow(int width, int height, std::string title) {
        return std::make_unique<Window>(width, height, title);
    }
    

    There's not much of a gain to call Window::createWindow instead of just calling std::make_unique<Window> directly. Also, the use of unique_ptr<Window>s is dubious. Window is final and it's not inheriting from anything. You should probably just store Windows directly in a container.