4

I have a type with a strict alignment requirement (due to AVX operations being used) that is larger than the platforms default alignment.

To make usage of this class simpler, I would like to specialize std::make_shared to always use a suitable allocator for this type.

Something like this:

namespace std{
    template<class... Args> inline
    auto make_shared<X, Args...>(Args&&... args){
        return std::allocate_shared(allocator_type<X, 32>, std::forward<Args>(args)...);
    }
}

My question is, is this allowed by the standard? Will it work as expected?

Emily L.
  • 5,673
  • 2
  • 40
  • 60
  • You can't use `alignas`? – dyp Jul 17 '15 at 10:40
  • 1
    @dyp No, or well yes, I already do, but it won't work for heap allocations because of [this](http://stackoverflow.com/questions/21895038/how-use-alignof-to-force-alignment-for-a-heap-allocation). This specialization is to make the alignment apply even on the heap in most common cases of the usage. – Emily L. Jul 17 '15 at 10:50

2 Answers2

10

From N4140 [namespace.std]/1 (emphasis mine):

The behavior of a C++program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

Since you are adding a template specialization which depends on a user-defined type, this is a valid extension of the std namespace.

However, as pointed out by @dyp, you can't partially specialize function templates. Your best options would be to explicitly specify the arguments to the X constructor (losing out on perfect-forwarding), or just write a make_shared_x function (losing out on consistency).

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Exactly what I wanted. I remembered there was a rule for doing this but didn't know the exact conditions under which it was legal. Do you see any downsides of doing this? – Emily L. Jul 17 '15 at 10:52
  • 1
    @EmilyL. You can't partially specialize a function template, and hence every specialization of a function template must be a full (or explicit) specialization. Therefore, you can't implement perfect forwarding using this loophole. (Nor can you use it generically for a set of types like a class template.) – dyp Jul 17 '15 at 10:57
  • Hah, I just saw template specialization in `std` and reached for this quote, overlooking the partial function template specialization. Maybe @EmilyL. would be better off just writing a `make_shared_x` function. – TartanLlama Jul 17 '15 at 11:01
  • @TartanLlama that's my current solution but I was hoping for something generic that doesn't explode when people use `make_shared` and expects it to work. – Emily L. Jul 17 '15 at 11:33
  • @Dyp silly me, I know that I can't partially specialize function templates... but I blame sudden acute temporary amnesia... I guess I really wanted this to work *sigh* – Emily L. Jul 17 '15 at 11:40
6

This is what I ended up doing to get a generic solution that doesn't involve a lot of boilerplate:

namespace xtd{
    template< typename T, std::size_t align = std::alignment_of<T>::value, typename... Args >
    std::shared_ptr<T> make_shared(Args&&... args){
        // Platform specific knowledge.
#if defined(_WIN64) || defined(_WIN32)
#if defined(_WIN64)
        const std::size_t default_alignment = 16;
#else
        const std::size_t default_alignment = 8;
#endif
#else
#error "Only windows for now"
#endif

        if (align > default_alignment) {
            typedef aligned_allocator<T, align> alloc_type;
            return std::allocate_shared<T, alloc_type>(alloc_type(), std::forward<Args>(args)...);
        }
        else {
            return std::make_shared<T>(std::forward<Args>(args)...);
        }
    }
}

Then I find Search & Replace std::make_shared with xtd::make_shared :)

I wish this would be in the standard...

Emily L.
  • 5,673
  • 2
  • 40
  • 60