Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::scoped_allocator_adaptor and a class with a constructor using std::allocator_arg_t

I find some words here http://en.cppreference.com/w/cpp/memory/scoped_allocator_adaptor/construct

if std::uses_allocator<T, inner_allocator_type>::value==true (the type T uses allocators, e.g. it is a container)

and if std::is_constructible<T, std::allocator_arg_t, inner_allocator_type, Args...>::value==true,

then calls

std::allocator_traits<OUTERMOST>::construct( OUTERMOST(*this),
                                             p,
                                             std::allocator_arg,
                                             inner_allocator(),
                                             std::forward<Args>(args)... );

So, I do a simple test

struct use_arg {
    template <typename Alloc>
    use_arg(std::allocator_arg_t, Alloc &, int i)
        { std::cout << i << " in use_arg()\n"; }
};

namespace std {

template <typename A> struct uses_allocator<use_arg, A>: true_type {};

} // namespace std

void test_scoped()
{
    std::scoped_allocator_adaptor<std::allocator<use_arg>> sa;
    auto p = sa.allocate(1);
    sa.construct(p, 4);
    sa.destroy(p);
    sa.deallocate(p, 1);
}

but gcc and clang give me these errors https://gist.github.com/anonymous/3e72754a7615162280fb

I also write use_a to replace use_arg. It could run successfully.

struct use_a {
    template <typename Alloc>
    use_a(int i, Alloc &) { std::cout << i << " in use_a()\n"; }
};

What makes these behaviors happen?

like image 819
linux40 Avatar asked Sep 25 '22 16:09

linux40


2 Answers

I think both libstdc++ and libc++ are doing exactly what the standard requires for the OP's example.

uses_allocator<use_arg, allocator<use_arg>> is true, but is_constructible<use_arg, allocator_arg_t, inner_allocator_type, int> is false because use_arg is not constructible from an rvalue allocator, so the construct call should be ill-formed.

However, I think it's a defect in the standard. Consider this type:

struct use_arg {
  using allocator_type = std::allocator<use_arg>;
  use_arg(allocator_type&&) { }
};

The uses_allocator and is_constructible traits are both true, but calls to scoped_allocator_adaptor::construct(pointer) will fail to compile.

It is inconsistent to check is_constructible<T, inner_allocator_type> (which tests for construction from an rvalue allocator) but then pass inner_allocator_type& (which is an lvalue), but that's what the standard says.

like image 115
Jonathan Wakely Avatar answered Sep 29 '22 07:09

Jonathan Wakely


Problem is, that you receives Alloc by reference.

std::is_constructible<T, std::allocator_arg_t, inner_allocator_type, Args...>::value==true

here in your case inner_allocator is just std::scoped_allocator_adaptor<std::allocator<use_arg>> and it cannot be converted to std::scoped_allocator_adaptor<std::allocator<use_arg>>&. You can just receives Alloc by value, or by const-reference.

like image 27
ForEveR Avatar answered Sep 29 '22 07:09

ForEveR