6

Here is a SSCCE:

#include <memory>
#include <vector>

template <class T> struct my_allocator : std::allocator<T> {
    //This overriding struct causes the error
    template <class U> struct rebind {
        typedef my_allocator<T> other;
    };

    //Ignore all this.
    typedef std::allocator<T> base;
    typename base::pointer allocate(typename base::size_type n, std::allocator<void>::const_pointer /*hint*/=nullptr) { return (T*)malloc(sizeof(T)*n); }
    void deallocate(typename base::pointer p, typename base::size_type /*n*/) { free(p); }
};

int main(int /*argc*/, char* /*argv*/[]) {
    std::vector<int,my_allocator<int>> vec;
    return 0;
}

GCC likes it.
ICC likes it.
Clang likes it.
Even MSVC 2013 likes it.
But MSVC 2015 RC spits out:

1>------ Build started: Project: Test Alloc, Configuration: Debug Win32 ------
1>  main.cpp
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\vector(579): error C2664: 'void std::_Wrap_alloc<my_allocator<int>>::deallocate(int *,unsigned int)': cannot convert argument 1 from 'std::_Container_proxy *' to 'int *'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\vector(579): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\vector(574): note: while compiling class template member function 'void std::_Vector_alloc<std::_Vec_base_types<_Ty,_Alloc>>::_Free_proxy(void)'
1>          with
1>          [
1>              _Ty=int,
1>              _Alloc=my_allocator<int>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\vector(541): note: see reference to function template instantiation 'void std::_Vector_alloc<std::_Vec_base_types<_Ty,_Alloc>>::_Free_proxy(void)' being compiled
1>          with
1>          [
1>              _Ty=int,
1>              _Alloc=my_allocator<int>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\vector(668): note: see reference to class template instantiation 'std::_Vector_alloc<std::_Vec_base_types<_Ty,_Alloc>>' being compiled
1>          with
1>          [
1>              _Ty=int,
1>              _Alloc=my_allocator<int>
1>          ]
1>  c:\users\ian mallett\desktop\test-alloc\main.cpp(18): note: see reference to class template instantiation 'std::vector<int,my_allocator<int>>' being compiled

Related program give similarly fishy sounding errors. Here are two:
error C2664: 'void std::_Wrap_alloc>::deallocate(int *,unsigned int)': cannot convert argument 1 from 'std::_Container_proxy *' to 'int *'
cannot convert argument 1 from 'std::_Wrap_alloc>' to 'const aligned_allocator &'

Boolean question: is this a bug? Iff it is, I will (try) to submit it.

[EDIT: as noted in the comments, this only occurs in debug mode. In release mode, it compiles and executes fine.] [EDIT: much simpler example]

Community
  • 1
  • 1
geometrian
  • 14,775
  • 10
  • 56
  • 132
  • Might be irrelevant, but adding declarations to namespace `std` is UB. – cpplearner Jul 19 '15 at 06:28
  • @cpplearner That's not true. There are exceptions, see [Extending the namespace `std`](http://en.cppreference.com/w/cpp/language/extending_std). – Captain Obvlious Jul 19 '15 at 06:55
  • @CaptainObvlious I don't think any of these exceptions apply here. – cpplearner Jul 19 '15 at 07:13
  • What if you turn off the diagnostic mode of the MSVC standard library? – Ulrich Eckhardt Jul 19 '15 at 09:05
  • I have edited the question to not put code in `std::`; the error still occurs. @UlrichEckhardt running in release mode causes it to compile/run successfully. – geometrian Jul 19 '15 at 09:32
  • 1
    It is just buggy code, you don't support rebind. Many places where you can find out how to create your own allocator type, nothing that should ever be repeated here :) – Hans Passant Jul 19 '15 at 09:37
  • My guess is that the diagnostic mode wraps your allocator to trap misuses. Assuming @Hans Passant is right (I have honestly no experience with implementing STL-style allocators), a functioning `rebind()` implementation in your allocator might be necessary for that, which would explain this behaviour and make it a bug in your code indeed. Have you tried diagnostic mode for other standard libraries (`#define _GLIBCXX_DEBUG`)? – Ulrich Eckhardt Jul 19 '15 at 12:43

1 Answers1

6

Boolean question: is this a bug?

false.


Although the template error given by MSVC here is surpassingly unhelpful, the error here is mine (reassuring since this version of the standard library is shipping today).

I created this allocator (and later, the reduced test case) from a variety of sources, which is why I assumed it was correct. However, as suggested in the comments, I checked again, this time exhaustively against the documentation.

The missing component here is one of the copy constructors (the template one that can't be auto-generated). This only shows up when the rebind struct is defined since the rebind struct overrides the same struct in the parent class (which, since it's in the parent class, ultimately causes the parent's copy constructor to be called, so there's no problem (except that it's technically wrong)).


The interesting thing here is that the error didn't occur until now. As I said, GCC, Clang, and MSVC 2013 all like it (even with their respective debug modes). It's just that none of these happened to call the template copy constructor. Nevertheless, it is specified by the standard, so again, the error is ultimately mine.

Congratulations to the MSVC compiler team, and sorry for the noise! :D

geometrian
  • 14,775
  • 10
  • 56
  • 132
  • Thanks for the clear answer, I ran into the exact same problem and you probably saved me some hours! – Benlitz Dec 26 '18 at 08:04