开发者

Generic allocator class without variadic templates?

I am trying to write a generic allocator class that does not really release an object's memory when it is free()'d but holds it in a queue and returns a previously allocated object if a new one is requested. Now, what I can't wrap my head around is how to pass arguments to the object's constructor when using my allocator (at least without resorting to variadic templates, that is). The alloc() function i came up with looks like this:

template <typename... T>
inline T *alloc(const &T... args) {
    T *p;

    if (_free.empty()) {
        p = new T(args...);
    } else {
        p = _free.front();
        _free.pop();

        // to call the ctor of T, we need to first call its DTor
        p->~T();
        p = new( p ) T(args...);
    }
    return p;
}

Still, I need the code to be compatible with tod开发者_StackOverfloway's C++ (and older versions of GCC that do not support variadic templates). Is there any other way to go about passing an arbitrary amount of arguments to the objects constructor?


When you need to target pre-C++0x compilers you need to provide pseudo-variadic templates, i.e. you need to provide a template function for every needed arity:

template<class T> 
T* alloc() { 
    /* ... */ 
}

template<class T, class A0> 
T* alloc(const A0& a0) { 
    /* ... */ 
}

/* ... */

You can use preprocessor metaprogramming though to handle the repititions, e.g. by using Boost.Preprocessor or by simply generating the functions using a simple script.

Following is a simple example using Boost.PP:

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>

template<class T>
T* alloc() {
    return new T;
}

#define FUNCTION_ALLOC(z, N, _) \
  template<class T, BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), class T)> \
  T* alloc(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(N), const T, &p)) { \
     return new T( \
       BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), p) \
     ); \
  }

BOOST_PP_REPEAT(10, FUNCTION_ALLOC, ~)

#undef FUNCTION_ALLOC

This generates you alloc() template functions for up to 10 arguments.


The pre-C++11 solution to the problem is to provide only one simple alloc function which constructs a copy of its argument. This is the way C++03 allocators and all the containers worked, for more than 20 years. Applying it to your code it becomes:

template <typename T>
inline T *alloc(const &T arg) {
    T *p;

    if (_free.empty()) {
        p = new T(arg);
    } else {
        p = _free.front();
        _free.pop();

        // to call the ctor of T, we need to first call its DTor
        p->~T();
        p = new( p ) T(arg);
    }
    return p;
}

And then you call it as:

// copy construct T into the allocator's memory:
instance_of_your_allocator.alloc(T(1, 2, 3));

The downside of this approach is that it requires a copy-constructor to be available, and its potentially a costly operation.

One more example:

vector<T> vec;
vec.push_back(T(1, 2, 3)); // C++03 way, uses move cons-tor in C++11 if possible.
vec.emplace_back(1, 2, 3); // C++11 way, constructs in-place
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜