Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Howard Hinnant's short_alloc (C++11 version) to compile in Visual C++ 2015

I'd like to be able to use a custom allocator with std::vector so that small data buffers (say, less than 1024 bytes) are stored on the stack, and only longer vectors are stored on the heap. As one who has a background in Fortran, it causes me physical pain each time I have to do a heap memory allocation to store half-a-dozen elements for the duration of a five-line subroutine!

Howard Hinnant has published his short_alloc allocator which does exactly what I'm looking for, and if I compile it with gcc it works a treat. However, in Visual C++ I can't get it to compile. In Visual C++ 2013 part of the problem was that that too many C++11 keywords were unsupported, but I still came up against a problem even when I had #DEFINE'd all of these away. Today I tried compiling in Visual C++ 2015 CTP 5, and the keywords are now all supported but the compilation ultimately fails for the same reason.

The problem is this: for a reason I can't claim to understand fully, Hinnant's code defaults the copy constructor but deletes the copy assignment operator:

short_alloc(const short_alloc&) = default;
short_alloc& operator=(const short_alloc&) = delete;

When attempting to compile, this triggers the following error in Visual C++:

xmemory0(892): error C2280: 'short_alloc<int,1024> &short_alloc<1024>::operator =(const short_alloc<1024> &)': attempting to reference a deleted function

What confuses me even more is that if I modify Hinnant's code to say

short_alloc(const short_alloc&) = default;
short_alloc& operator=(const short_alloc&) = default;

...then I still obtain exactly the same error message.

For reference, here is my test code:

#include <iostream>
#include <vector>
#include "short_alloc.h"

void populate_the_vector(std::vector<int, short_alloc<int, 1024> > &theVector)
{
    arena<1024> B;
    std::vector<int, short_alloc<int, 1024> > anothertestvec{(short_alloc<int, 1024>(B))};
    anothertestvec.resize(10);
    for (int i=0; i<10; ++i)
    {
        anothertestvec[i] = i;
    }    
    theVector = std::move(anothertestvec);  // Actually causes a copy, as the Arenas are different
}

int main()
{
    arena<1024> A;
    std::vector<int, short_alloc<int, 1024> > testvec{(short_alloc<int, 1024>(A))};
    populate_the_vector(testvec);
    printf("Testvec(10)=%d\r\n", testvec[5]);
    return 0;
}

The compilation error goes away if I comment-out the line saying

theVector = std::move(anothertestvec);

so obviously the underlying problem is that Visual C++ approaches the copy in a different way from gcc. Even so, I'm at a loss for how to proceed from here. Is there a way to get this to work in Visual C++?

like image 392
Eos Pengwern Avatar asked Feb 03 '15 10:02

Eos Pengwern


1 Answers

The simplest hack that I can think of is replacing

short_alloc& operator=(const short_alloc&) = delete;

with

short_alloc& operator=(const short_alloc&) 
{ 
    assert(false && "this should never be called"); 
    return *this; 
};

It looks like a pretty dangerous hack, but it's actually not that bad in this particular case, and here's why:

The reason the original version doesn't compile in VC++ is that its standard library implementation of std::vector's move assignment operator makes the classic mistake of testing std::allocator_traits<...>::propagate_on_container_move_assignment::value using an if() statement.

It does the proper check and doesn't assign the allocator if the trait value is false (and moves the elements individually to the other side if the allocators are different, as requested by the standard), but the code on the if() branch still has to compile, even though it will never be reached for this type of allocator.

So, that assignment operator will never be called by the container's implementation at runtime, and that's why the hack is safe in this particular case.

(The funniest thing is that, one line below that if(), the actual moving is implemented properly with a helper function using tag dispatch...)


This is based on the standard library implementation that comes with Visual C++ 2013 Update 4.

Update: As reported by the OP in the comments, VC14 CTP5 has the same problem.


Update 2: As indicated in the comments for the bug report, the fix for this issue will be available in the final version of Visual C++ 2015.

like image 182
bogdan Avatar answered Oct 22 '22 19:10

bogdan