2

I made a class to encapsulate some functionality from OpenGL textures, my requirements are pretty simple so I don't need much more than being able to control the texture data and types. My class is the following (simplified):

class Texture
{
    std::vector<unsigned char> _data;
    [...]
    public:
        Texture(std::vector<unsigned char> data, vec2 size, GLint internal_format, GLenum format, GLenum type);
        Texture(const Texture& other)
        {
            //copies the data and creates a new buffer with OpenGL
        }
        Texture(Texture&& other) noexcept
        {
            //moves the data, copies the buffer id and set the old buffer id to 0
        }
}

Now I have another class that manages some textures using a std::vector, in an effort to make most of my memory management automatic I realized that I don't need to allocate these textures in the heap, so the vector type is std::vector<Texture>. The problem is that when I add new textures to the vector it is making copies instead of moving. I found two solutions, if delete the copy constructor/assignment operator, std::vector moves. But I need to copy in some situations. I can also just reserve the vector since I know how many textures I'll be keeping. The problem with both these solutions is that I don't know what other cases I may have and I expect move semantics to work correctly.

I read other other answers about this problem, but using noexcept doesn't solve the problem, and as I said, if delete the copy constructor the move constructor will be chosen. Even if I remove the noexcept and throw something from it. Is this correct? Should the compiler use an unsafe move constructor if there's no copy constructor available? Also, why won't the compiler choose the safe move constructor I'm providing?

(I'm using GCC 4.7)

[edit]

I made an example: http://liveworkspace.org/code/4bzSMD$6 and realized that if I leave the default destructor, then the move constructor will be used, but If I provide a destructor, then it copies. Why is that?

Community
  • 1
  • 1
Luke B.
  • 1,258
  • 1
  • 17
  • 28
  • 1
    [Vectors items are on the heap regardless](http://stackoverflow.com/a/1642917/1711796). Pointers or `boost::shared_ptr` may make your life easier (and not unsafe). – Bernhard Barker Feb 13 '13 at 07:00
  • @Dukeling I was hoping to avoid complicating my code if there's no need, unless I'm wrong to assume the code I posted should work. And what I meant about not keeping them on the heap is that they have automatic duration, and not that they would be on the stack. – Luke B. Feb 13 '13 at 07:06
  • what do you mean with *unsafe move*? your move constructor can throw?? – BЈовић Feb 13 '13 at 07:12
  • Note `boost::shared_ptr` also has "automatic duration" (or is auto-memory-managed) and managing memory for pointers safely is surprisingly easy if you done correctly. I would imagine one should try to avoid unsafe code at all costs, even if it does complicate your code a little. – Bernhard Barker Feb 13 '13 at 07:14
  • @LukeB. As a side note, the "safety" you're espousing is called [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) and is *highly* encouraged. Excellent that you're thinking of it in design and practice. Dynamic allocation is fine, so long as it's ownership is call-stack managed. a `std::shared_ptr`, `boost::shared_ptr`, etc is not out of the realm of possible solutions if you're truly worried about data copying, and should be on the pile of considerations to mull over. – WhozCraig Feb 13 '13 at 07:33
  • Note: normally you should not have to implement the copy and move constructors by yourself; what is so special to your class that the compiler generated version is not suitable ? – Matthieu M. Feb 13 '13 at 08:48
  • Have you tried to use yourTextureVector.insert(std::move(yourTextureElement)) ? to force the vector container to move the element instead of copying it ? – Matei Suica Feb 13 '13 at 12:18
  • @BЈовић not normally, no, but if there's no copy constructor even a throwing move constructor is chosen. – Luke B. Feb 13 '13 at 13:49
  • @MatthieuM. I am managing the texture in the video card, copies have to be 'real' copies, where a new instance of the texture is created, I also can't delete a texture if there's another object using it, so I'm forcing the user to either make a copy or get a reference. – Luke B. Feb 13 '13 at 13:51
  • @Dukeling how is my code unsafe? – Luke B. Feb 13 '13 at 13:53
  • @QuadroQ The problem is not with the inserted element, but with all the others being copied when the vector is growing. – Luke B. Feb 13 '13 at 13:57
  • @WhozCraig Smart pointers are indeed a solution, but unless my understanding of the C++ move semantics is completely wrong, shouldn't my example be working? – Luke B. Feb 13 '13 at 14:01
  • @LukeB. You said it's unsafe, I was just being a sheep. – Bernhard Barker Feb 13 '13 at 14:06

1 Answers1

0

I still don't know why GCC chooses an unsafe move constructor if not copy constructor is provided, but the move problem is from an explicit destructor on GCC 4.7 not being noexcept(true) by default, so when there's a copy available, it will be seen as safer by the compiler (since copying doesn't destroy the element.)

More on that here What is the criteria that the compiler uses to decide if a move operation is safe?

Community
  • 1
  • 1
Luke B.
  • 1,258
  • 1
  • 17
  • 28