qloq
qloq

Reputation: 1283

Is it possible to make custom allocator to be acceptable by container constructor without specifying it in template arguments

Here is an example of allocator from cppreference:

#include <cstdlib>
#include <limits>
#include <new>

template<class T>
struct Mallocator
{
    typedef T value_type;
    Mallocator() = default;
    template<class U>
    constexpr Mallocator(const Mallocator<U>&) noexcept
    {
    }

    [[nodiscard]] T* allocate(std::size_t n)
    {
        if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
            throw std::bad_array_new_length();

        if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) {
            return p;
        }

        throw std::bad_alloc();
    }

    void deallocate(T* p, std::size_t n) noexcept { std::free(p); }
};

template<class T, class U>
bool operator==(const Mallocator<T>&, const Mallocator<U>&)
{
    return true;
}

template<class T, class U>
bool operator!=(const Mallocator<T>&, const Mallocator<U>&)
{
    return false;
}

It can be used as in examples 1 and 2 in the following snippet:

#include "mallocator.h"

#include <vector>

int mainF()
{
    std::vector<int, Mallocator<int>> v; // 1
    Mallocator<int>                   a;
    std::vector<int, Mallocator<int>> v2 { a }; // 2
    std::vector<int, Mallocator<int>> v3 { a }; // 2

    // std::vector<int> v4 { a }; // 3 (compilation error)

    return 0;
}

Is it possible to conform custom allocator so it will work as in example 3. If yes, how to do it?

Upvotes: -1

Views: 103

Answers (2)

Ahmed AEK
Ahmed AEK

Reputation: 18090

std::vector<int> is implicitly std::vector<int, std::allocator<int>>, if you omit the second template argument it will be the default one, it will not be deduced from the constructor.

CTAD is all or none, so you can either generate your own alias, or apply CTAD to All template arguments.

#include "mallocator.h"
#include <vector>

template <typename T>
using MyVector = typename std::vector<T, Mallocator<T>>;

int main()
{
    std::vector<int, Mallocator<int>> v; // 1
    Mallocator<int>                   a;
    std::vector<int, Mallocator<int>> v2 { a }; // 2
    std::vector<int, Mallocator<int>> v3 { a }; // 2

    std::vector v4 { a }; // let CTAD deduce both

    MyVector<double> v5{ a };

    return 0;
}

godbolt example

note that you cannot have std::vector<double> v5{a}; because CTAD isn't invoked, and you will have std::vector<double, std::allocator<double>> v5{a}, which cannot compile.

Upvotes: 0

sedor
sedor

Reputation: 138

No, class template argument deduction (CTAD) doesn't allow this. But you can create a wrapper function and use partial template argument deduction with this function.

Something like this:

#include <memory>
#include <vector>

template <typename T, typename Allocator>
auto get_vector(const Allocator& a) {
    return std::vector<T, Allocator>{a};
}

int main() {
    // Insert your own allocator here.
    std::allocator<int> some_random_allocator;
    auto vec = get_vector<int>(some_random_allocator);
}

Upvotes: 0

Related Questions