Universal OpenGL object RAII wrapper class I created a universal OpenGL object RAII wrapper class, that only takes care of object creation and destruction. Here's my code and reasoning behind it:
I first wrote a class that would take glCreate/Delete*() function pointers as constructor arguments, but I quickly realized that's not the right way to go. I'd have to store the glDelete*() function pointer in each object (waste of memory + dereference overhead). Additionally, OpenGL functions have three different forms (see specializations section, further in my code), so that would require 3 different pointer types - ugly. I decided to change my approach.
So, here I am with this template code. As far as I'm concerned, there should be no unnecessary OpenGL call overhead. The class is standard layout, only contains the object ID and there's no virtual stuff involved, so that seems pretty nice. Also, classes wrapping different OpenGL object types cannot be assigned to each other, because template arguments differ.
I'd like to hear your remarks according my code, the class design and the reasoning behind it. Improvement ideas are also welcome.
This is the main code:
/**
    \brief Serves as a RAII wrapper for OpenGL objects
    \note This class provides no OpenGL error checking - this is up to derived classes.
    \template T is object type as in glObjectLabel
*/
template &lt;GLenum T&gt;
class gl_object
{
    public:
        //! Object type used for labeling
        const static GLenum object_type = T;

        inline gl_object( );
        inline explicit gl_object( GLenum target );

        inline ~gl_object( );

        // Deleted copy constructor and copy assignment operator
        gl_object( const gl_object &amp;src ) = delete;
        gl_object &amp;operator=( const gl_object &amp;rhs ) = delete;

        // Move semantics with source invalidation
        gl_object( gl_object &amp;&amp;src );
        gl_object &amp;operator=( gl_object &amp;&amp;rhs );

        //! Allows casting to object's ID (GLuint)
        inline operator GLuint( ) const noexcept;

        //! Returns object's ID
        inline GLuint id( ) const noexcept;

    protected:
        //! The object ID
        GLuint m_id;
};

//! Move constructor with source invalidation
template &lt;GLenum T&gt;
gl_object&lt;T&gt;::gl_object( gl_object&lt;T&gt; &amp;&amp;src ) :
    m_id( src.m_id )
{
    src.m_id = 0;
}

//! Move assignment operator with source invalidation
template &lt;GLenum T&gt;
gl_object&lt;T&gt; &amp;gl_object&lt;T&gt;::operator=( gl_object&lt;T&gt; &amp;&amp;rhs )
{
    // Prevent self-move
    if ( this != &amp;rhs )
    {
        m_id = rhs.m_id;
        rhs.m_id = 0;
    }

    return *this;
}

// Allows cast to GLuint
template &lt;GLenum T&gt;
gl_object&lt;T&gt;::operator GLuint( ) const noexcept
{
    return m_id;
}

// Used for acquiring object's ID
template &lt;GLenum T&gt;
GLuint gl_object&lt;T&gt;::id( ) const noexcept
{
    return m_id;
}

Then, there are many very similar template specializations. Here are the most interesting ones:
// Specializations for GL_BUFFER
template &lt;&gt;
gl_object&lt;GL_BUFFER&gt;::gl_object( )
{
    glCreateBuffers( 1, &amp;m_id );
}

template &lt;&gt;
gl_object&lt;GL_BUFFER&gt;::~gl_object( )
{
    glDeleteBuffers( 1, &amp;m_id );
}

// Specializations for GL_TEXTURE
template &lt;&gt;
gl_object&lt;GL_TEXTURE&gt;::gl_object( GLenum target )
{
    glCreateTextures( target, 1, &amp;m_id );
}

template &lt;&gt;
gl_object&lt;GL_TEXTURE&gt;::~gl_object( )
{
    glDeleteTextures( 1, &amp;m_id );
}

// Specializations for GL_SHADER
template &lt;&gt;
gl_object&lt;GL_SHADER&gt;::gl_object( GLenum type )
{
    m_id = glCreateShader( type );
}

template &lt;&gt;
gl_object&lt;GL_SHADER&gt;::~gl_object( )
{
    glDeleteShader( m_id );
}

// Specializations for GL_PROGRAM
template &lt;&gt;
gl_object&lt;GL_PROGRAM&gt;::gl_object( )
{
    m_id = glCreateProgram( );
}

template &lt;&gt;
gl_object&lt;GL_PROGRAM&gt;::~gl_object( )
{
    glDeleteProgram( m_id );
}
Ensuring you have distinct types
You said this in the comments:

I thought that use of templates instead of a common base class would better emphasize that these objects shouldn't be mixed together (now they are completely distinct types).

Two classes that derive from the same base class are also two distinct types. Furthermore, you can make the constructor of the base class protected, so you cannot instantiate a bare base class, and you can use protected or private inheritance to ensure you cannot get a pointer to the base class from a derived class. For example:
class base {
protected:
    base() = default;
};

class derived: base {
    ...
};

derived foo;      // OK
base bar;         // Error: constructor is protected
base *baz = &amp;foo; // Error: base is inaccessible

The problem with template specialization
Your construction using template specialization has a flaw. The problem is that all member functions you might ever want to use in specializations need to be present in the default template. This is why you have:
template &lt;GLenum T&gt;
class gl_object
{
    ...
    inline gl_object( );
    inline explicit gl_object( GLenum target );
    ...
};

The second constructor that takes the target parameter is only used for gl_object&lt;GL_TEXTURE&gt;, and in fact you must have a target for the texture in order to call glCreateTextures(). The problem is now that I can do the following:
gl_object&lt;GL_BUFFER&gt; buffer(GL_TEXTURE_2D);
gl_object&lt;GL_TEXTURE&gt; texture;

The above statements will compile without errors (but perhaps with a warning), because you promised both constructors would exist. Only the linker will give an error, because there is no implementation of those constructors. But in a large project it might be hard to track down this issue.
You won't have this problem with derived classes, since you have full control over which public member functions (including constructors) each derived class has.
Alternatives without templates
There are two ways to implement this without templates. Either you say that, for example, a gl_texture &quot;is a&quot; gl_object, and use class inheritance, or you say that a gl_texture &quot;has a&quot; gl_name (the OpenGL term for the GLuint identifier given to objects), and use composition. In the first case, you'd structure your code as:
class gl_object {
protected:
    GLuint m_id;

    gl_object() = default;

    // delete copy constructors
    // add move semantics
    // access the ID
};

class gl_buffer: gl_object {
public:
    gl_buffer() {
        glCreateBuffers(1, &amp;m_id);
    }
    ...

    // Enable the default constructor and assignment operator:
    gl_buffer(gl_buffer &amp;&amp;other) = default;
    gl_buffer &amp;operator=(gl_buffer &amp;&amp;other) = default;
};

In the second case, you would write:
class gl_name {
private:
    GLuint m_id;

public:
    // delete copy constructors
    // add move semantics
    // access the ID

    GLuint &amp;get() {
        return m_id;
    }
};

class gl_buffer {
private:
    gl_name m_id;

public:
    gl_buffer() {
        glCreateBuffers(1, &amp;m_id.get());
    }
    ...

    // Enable the default constructor and assignment operator:
    gl_buffer(gl_buffer &amp;&amp;other) = default;
    gl_buffer &amp;operator=(gl_buffer &amp;&amp;other) = default;
};

In the second case, since you deleted the copy constructors for gl_name, classes that use that as a member variable will also no longer be default copy constructable.
The main drawback of either two cases is that you need to implement the move constructors and move assignment operators in each derived class. In this case the default move constructor and move assignment operator would have worked just fine, but they have been implicitly deleted. To re-enable them, just explicitly add the default move constructor and move assignemnt operator.