0

There is an allocator:

template<typename T>
class pool_allocator {
public:
    using value_type = T;
    using pointer = value_type *;

    /* Default constructor */
    constexpr pool_allocator( void ) noexcept = default;

    /* Converting constructor used for rebinding */
    template<typename U>
    constexpr pool_allocator( const pool_allocator<U> & ) noexcept {}

    [[nodiscard]] pointer allocate( size_t n, [[maybe_unused]] const pointer hint = nullptr ) const noexcept {
        return get_pool().allocate( n );
    }

    void deallocate( pointer ptr, size_t n ) const noexcept {
        get_pool().deallocate( ptr, n );
    }

private:
    /* Must be defined in particular .cpp files */
    static auto & get_pool( void ) noexcept;
};

which expects the definition of get_pool() for particular types in .cpp files. The pool_allocator allocates the instance within the memory pool.

struct cpu { ... };

To define the pool itself - the area in memory where the instances are located may look like:

memory_pool<cpu, 4> cpu_storage;

template<>
auto & pool_allocator<cpu>::get_pool( void ) noexcept {
    return cpu_storage;
}

While attempting to use that like:

class process {
    unique_ptr<cpu> m_cpu { nullptr };
};

I face the trouble GCC reports:

error: use of 'static auto& ReVolta::pool_allocator<T>::get_pool() [with T = cpu]' before deduction of 'auto'
get_pool().deallocate( ptr, n ); // line of code within the pool_allocator<T>::deallocate(...) implemnetation 

May I kindly ask for help?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Martin Kopecký
  • 912
  • 5
  • 16

1 Answers1

1

I suspect you have either defined (i.e provide the body of) and used the get_pool function in a separate c++ file, or they are in the same file but the usage comes first.

In the first case, you might have done something like:

// f.hpp
auto f();

// f.cpp
auto f() { return 42; }

// main.cpp
#include "f.hpp"
int main() { return f(); }

Now think like the compiler who is trying to compile main.cpp. Since you are a c++ compiler, you can only see the content of f.hpp and main.cpp. But how then could you tell the exact return type of f?

In the second case, you probably have done something like (in a single cpp file):

auto f();

int main() {
    return f();
}

auto f() {
    return 42;
}

When you write return f(); the compiler still needs to know the return type of 42. But it has not seen the definition yet. If you reverse the order, however:

auto f();

auto f() {
    return 42;
}

int main() {
    return f();
}

It would compile.


Either way, it is still unconventional to have declared a function in the template but not to follow it up immediately with a definition (either inline with the class definition or immediately following the class definition in the header), since if you define the members of your template inside a cpp file, you would not be able to use it in other cpp files.

ph3rin
  • 4,426
  • 1
  • 18
  • 42
  • The main motivation to use such maybe unconventional way is to have a single source emty (no member variables) allocator and to have an option to define various memory pools for particular types. For instance, `pool_allocator` shall allocate within memory_pool, for other type `sample` shall use memory_pool, etc. The problem is, the size of the pool is available only in the pool itself and is not known to the allocator (would be glad to know how to make it available to the allocator, may solve the problem) – Martin Kopecký Jun 29 '21 at 18:34
  • @MartinKopecký You could use something similar in principle to https://en.cppreference.com/w/cpp/memory/allocator_traits – ph3rin Jun 29 '21 at 19:55
  • What exactly do you mean? It is unclear to me how which principle of allocator_traits shall I use? – Martin Kopecký Jun 29 '21 at 20:02