3

I've recently replaced some Vector/Matrix classes with ones that use SSE, and am now making sure that the memory is aligned properly.

Following the advice in the answer to this question, I've replaced operator new/delete for the classes that require it and have started work on a custom allocator to use with STL containers - however, there seems to be some conflict between the two:

To get started, I've simply copied and pasted the sample allocator class from here, which compiles fine when I use it with a std::vector of the types in question without my custom new/delete, but when I replace those functions, I get an error "no matching function for call to 'operator new'" from the construct() function,

void    construct(pointer p, const T& t)    { new(p) T(t); }

I guess the fact that I've replaced the "usual" new has somehow obscured placement new? However, given that I can't write my own placement new for it to pick up, I'm not really sure what to do... I'm new (NPI) to the whole custom memory allocation thing, so any advice would be much appreciated!

I'm compiling on Linux using Clang v3.4 (or gcc 4.1.2); not using C++11.

Many thanks.

Community
  • 1
  • 1
YamLady
  • 31
  • 3

3 Answers3

4

The canonical allocator::construct calls ::new((void *)p) T(val)

By omitting the ::, you've let name lookup start with the class scope of T, where it found your class-scoped operator new and did not proceed any further (name lookup stops at the first scope in which any matching names are found, even if a better candidate exists in some enclosing scope)

(the cast to void is in case the user sneaks in a global almost-placement new overload that takes a non-void pointer parameter)

PS: as correctly pointed out in the comments, "given that I can't write my own placement new" is wrong assumption. You cannot replace a global placement-new, but you can certainly write a class-specific placement new, which would then be picked up by class-scope lookup. Check out cppreference for a summary on allocation functions.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • To fix this, `::new` **or** implement an overridden placement `new` for your class. – Yakk - Adam Nevraumont Sep 18 '14 at 18:12
  • Ah, yeah, that makes sense... Thanks! For some reason I completely missed the note on ccpreference that points this out: "Even though placement new (overloads 5 and 6) cannot be replaced, a function with the same signature may be defined at class scope as described above"... Oops. – YamLady Sep 19 '14 at 10:53
1

I'd suggest using Boost's aligned_allocator:

#include <boost/align/aligned_allocator.hpp>
#include <immintrin.h>
#include <vector>

struct m128i {
    // FIXME: ctors/opers with intrinsics would be nice (required?)
    __m128i data;
}

int main()
{
    std::vector<m128i, boost::alignment::aligned_allocator<m128i, 16> > v;
    v.emplace_back();
}

Note: I've updated this to use a structure which wraps the intrinsic member. There are plenty of libraries that do this. The reason to do this is simple: the vector_size attribute is what distinguishes __m128i from __m256i from __m512i, and templates ignore type attributes, so I believe they would all end up using the same expansion, which is "long long" (or float/double in the case of the non-i, and d types).

James Cape
  • 704
  • 3
  • 11
0

I would use my starting point as the allocator given here: http://en.cppreference.com/w/cpp/concept/Allocator. Which is actually a minimal allocator. In particular, you don't need to write construct, at all. allocator_traits, if it does not find that your allocator has a construct method, will simply call placement new for you, correctly scoping the call with :: (as Cubbi notes) so you don't have this issue: http://en.cppreference.com/w/cpp/memory/allocator_traits/construct.

I probably wouldn't write custom new/delete's at all. It should suffice just to write a custom allocator, and have your Vector/Matrix classes manage their data via a std::vector using your custom allocator.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72