jeudi 24 décembre 2020

Extended copy constructor with custom allocators

In my experiments with scoped_allocator_adaptors using x86_64 gcc/clang trunk, I've run into an issue where the code snippet below compiles if the extended copy constructor is enabled but without it results is_constructible static assertion below. The compiler complains of the extended copy constructor not being defined even though it is never invoked when enabled. Can someone help answer why that might be the case?

#include <iostream>
#include <vector>
#include <scoped_allocator>

template <typename T>
struct MyAlloc
{
    using value_type = T;
    MyAlloc(const std::string &scope) noexcept : _scope(scope)  {} 
    // Rebinding allocatos to different type 
    template <class U> MyAlloc(MyAlloc<U> const& other) noexcept : _scope(other._scope)  {}
    value_type*  allocate(std::size_t n) noexcept
    {
        std::cout << "Allocating " << n << " objects within " << _scope << " from " << __PRETTY_FUNCTION__ << std::endl;
        return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
    }
    void deallocate(value_type* p, std::size_t n) noexcept
    {
        std::cout << "Deallocating " << n << " objects within " << _scope << " from " << __PRETTY_FUNCTION__ << std::endl;
        ::operator delete(p);
    }
    std::string _scope;
};

template <typename T>
using MyAllocAdaptor = std::scoped_allocator_adaptor<MyAlloc<T>>;

template <typename T> //  adaptor to propagate
using myvec = std::vector<T, MyAllocAdaptor<T>>;

template <typename T> 
using bstr = std::basic_string<T, std::char_traits<T>, MyAlloc<T>>;

using mystring = bstr<char>;

// Example struct with multiple nested containers with different types
// More realistic example that single type containers 
class S
{
    int z;
    mystring str;
    myvec<int> vec;
    
public:
    // If not public, the allocator aware constructor is not invoked
    // limiting the propagation of the allocator
    using allocator_type = MyAllocAdaptor<S>;

    S(allocator_type alloc) :
        z(1),
        str("This string should really not have SBO....", std::allocator_traits<allocator_type>::rebind_alloc<char>(alloc)),
        vec(std::allocator_traits<allocator_type>::rebind_alloc<int>(alloc))        
    {        
        vec.push_back(10);
    }
    S() : S(allocator_type("NoScope")) {
        std::cout << "Should not be invoked " << __PRETTY_FUNCTION__ << std::endl;
    }
    ~S()
    {
        std::cout << __PRETTY_FUNCTION__ << " Z " << z << std::endl;
    }
#if 0 // If this is defined things compile 
    S(const S& other, allocator_type alloc = {}) : 
        str(other.str, std::allocator_traits<allocator_type>::rebind_alloc<char>(alloc)),
        vec(other.vec, std::allocator_traits<allocator_type>::rebind_alloc<int>(alloc))            
    {
     
    }
#endif
};

int main()
{
    MyAlloc<S> alloc("scope1");
    myvec<S> vec(alloc);
    vec.emplace_back();
}

Compiler error when the extended copy constructor is not defined (even though it's never invoked when defined)

In file included from <source>:3:
In file included from /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/scoped_allocator:39:
In file included from /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/tuple:40:
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/uses_allocator.h:96:7: error: static_assert failed due to requirement '__or_<std::is_constructible<S, std::allocator_arg_t, const std::scoped_allocator_adaptor<MyAlloc<S>> &, const S &>, std::is_constructible<S, const S &, const std::scoped_allocator_adaptor<MyAlloc<S>> &>>::value' "construction with an allocator must be possible if uses_allocator is true"
      static_assert(__or_<
      ^             ~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/scoped_allocator:377:8: note: in instantiation of template class 'std::__uses_alloc<true, S, std::scoped_allocator_adaptor<MyAlloc<S>>, const S &>' requested here
            = std::__use_alloc<_Tp, inner_allocator_type, _Args...>(__inner);
              ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/alloc_traits.h:247:8: note: in instantiation of function template specialization 'std::scoped_allocator_adaptor<MyAlloc<S>>::construct<S, const S &>' requested here
        { __a.construct(__p, std::forward<_Args>(__args)...); }
              ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/stl_uninitialized.h:318:16: note: in instantiation of function template specialization 'std::allocator_traits<std::scoped_allocator_adaptor<MyAlloc<S>>>::construct<S, const S &>' requested here
            __traits::construct(__alloc, std::__addressof(*__cur), *__first);
                      ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/stl_uninitialized.h:353:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<const S *, S *, std::scoped_allocator_adaptor<MyAlloc<S>>>' requested here
      return std::__uninitialized_copy_a
                  ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/vector.tcc:473:10: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<S *, S *, std::scoped_allocator_adaptor<MyAlloc<S>>>' requested here
                = std::__uninitialized_move_if_noexcept_a
                       ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/vector.tcc:121:4: note: in instantiation of function template specialization 'std::vector<S, std::scoped_allocator_adaptor<MyAlloc<S>>>::_M_realloc_insert<>' requested here
          _M_realloc_insert(end(), std::forward<_Args>(__args)...);
          ^
<source>:78:9: note: in instantiation of function template specialization 'std::vector<S, std::scoped_allocator_adaptor<MyAlloc<S>>>::emplace_back<>' requested here
    vec.emplace_back();
        ^
1 error generated.
Compiler returned: 1

Aucun commentaire:

Enregistrer un commentaire