[ Answer: While not using push_back (as described in the duplicate thread), improper usage of emplace_back here was triggering the same temp-object-desctructor-used issue. Using emplace_back() instead of emplace_back(Demo()) solved the problem. ]
I'll leave this question un-edited below to show the mistake I had missed, and why this question is having the same problem as the marked-as-duplicate thread, despite using emplace_back instead of push_back.]
(The example below might be clearer than this explanation.)
In a header file I have a struct whose constructor calls glGenVertexArrays() on a member GLuint. In a source file, I emplace_back() an object of that struct into a std::vector.
For some reason, this causes glBindVertexArray() to fail with Invalid Operation.
As a workaround, I can create another function in that struct, and call glGenVertexArrays() from that function instead, and things work. This suggests constructor usage is a problem. Alternatively I can leave the constructor as-is and not use a vector, and things also work. That suggests constructor usage isn't the [only] problem. I can't understand it so my question: Why is vector + constructor usage forcing this workaround?
Example A doesn't work, but Example B does.
ExampleA.h (fails)
    struct Demo
    {
        Demo()
        { 
            glGenVertexArrays(1, &vaoID); 
        }
        GLuint vaoID;
    };
    std::vector<Demo> m_demos;
ExampleA.cpp
    m_demos.emplace_back(Demo());
    glBindVertexArray(m_demos[0].vaoID);   // Fails (but would work if no vector used)
ExampleB.h (works)
    struct Demo
    {
        Demo() { /* do nothing */ }
        void GenerateBuffers()             // Added this workaround
        { 
            glGenVertexArrays(1, &vaoID); 
        }
        GLuint vaoID;
    };
ExampleB.cpp
    m_demos.emplace_back(Demo());
    m_demos[0].GenerateBuffers();          // Added this workaround..
    glBindVertexArray(m_demos[0].vaoID);   // .. which works as desired.
