首页 > 解决方案 > 具有自定义分配器的扩展复制构造函数

问题描述

在我使用 x86_64 gcc/clang 主干对 scoped_allocator_adaptors 进行的实验中,我遇到了一个问题,如果启用了扩展复制构造函数但没有它会导致下面的 is_constructible 静态断言,则下面的代码片段可以编译。编译器抱怨未定义扩展的复制构造函数,即使它在启用时从未被调用。有人可以帮助回答为什么会这样吗?

#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();
}

未定义扩展复制构造函数时的编译器错误(即使定义时从未调用它)

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

标签: c++c++11templatesc++17allocator

解决方案


推荐阅读