Search code examples
c++shared-ptrsmart-pointersc++20

Error using `make_shared<U[]>( std::size_t N )`


I am trying to implement a fixed size multi-dimensional array whose size is determined at runtime. with the (2) overload of make_shared (template<class T> shared_ptr<T> make_shared(std::size_t N) // T is U[]). However, I am facing compilation errors (logs below). The error is not present if I change the shareds to their unique counterparts. My question is,

  • What is this error about?
  • Why unique works?
  • Any better way to implement such runtime-fixed multi-dimentional array container?

Minimal working example:

#include <memory>
#include <iostream>
int main() {
    typedef int cell_t;
    std::size_t x, y;
    std::cin >> y >> x;
    auto layout = std::make_shared<std::shared_ptr<cell_t[]>[]>(y);
    for (std::size_t i = 0; i < y; i += 1) {
        layout[i] = std::make_shared<cell_t[]>(x);
    }
    return 0;
}

The error message from g++-10 is as follows (note suppressed for brevity)

In file included from /usr/include/c++/10/ext/alloc_traits.h:34,
                 from /usr/include/c++/10/bits/stl_uninitialized.h:67,
                 from /usr/include/c++/10/memory:66,
                 from test_shared.cpp:1:
/usr/include/c++/10/bits/alloc_traits.h: In instantiation of ‘static constexpr void std::allocator_traits<std::allocator<_Up> >::construct(std::allocator_traits<std::allocator<_Up> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::shared_ptr<int []>; _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []> []; std::allocator_traits<std::allocator<_Up> >::allocator_type = std::allocator<std::shared_ptr<int []> []>]’:
/usr/include/c++/10/bits/shared_ptr_base.h:551:39:   required from ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []>; _Alloc = std::allocator<std::shared_ptr<int []> []>; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10/bits/shared_ptr_base.h:682:16:   required from ‘std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = std::shared_ptr<int []>; _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10/bits/shared_ptr_base.h:1371:71:   required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []> []; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10/bits/shared_ptr.h:408:59:   required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []> []]’
/usr/include/c++/10/bits/shared_ptr.h:859:14:   required from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = std::shared_ptr<int []> []; _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}]’
/usr/include/c++/10/bits/shared_ptr.h:875:39:   required from ‘std::shared_ptr<_Tp> std::make_shared(_Args&& ...) [with _Tp = std::shared_ptr<int []> []; _Args = {long unsigned int&}]’
test_shared.cpp:7:63:   required from here
/usr/include/c++/10/bits/alloc_traits.h:514:21: error: no matching function for call to ‘construct_at(std::shared_ptr<int []>*&, long unsigned int&)’
  514 |    std::construct_at(__p, std::forward<_Args>(__args)...);
      |    ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/10/memory:65,
                 from test_shared.cpp:1:
/usr/include/c++/10/bits/stl_construct.h: In substitution of ‘template<class _Tp, class ... _Args> constexpr decltype (::new(void*(0)) _Tp) std::construct_at(_Tp*, _Args&& ...) [with _Tp = std::shared_ptr<int []>; _Args = {long unsigned int&}]’:
/usr/include/c++/10/bits/alloc_traits.h:514:21:   required from ‘static constexpr void std::allocator_traits<std::allocator<_Up> >::construct(std::allocator_traits<std::allocator<_Up> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::shared_ptr<int []>; _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []> []; std::allocator_traits<std::allocator<_Up> >::allocator_type = std::allocator<std::shared_ptr<int []> []>]’
/usr/include/c++/10/bits/shared_ptr_base.h:551:39:   required from ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []>; _Alloc = std::allocator<std::shared_ptr<int []> []>; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10/bits/shared_ptr_base.h:682:16:   required from ‘std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = std::shared_ptr<int []>; _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10/bits/shared_ptr_base.h:1371:71:   required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []> []; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10/bits/shared_ptr.h:408:59:   required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}; _Tp = std::shared_ptr<int []> []]’
/usr/include/c++/10/bits/shared_ptr.h:859:14:   required from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = std::shared_ptr<int []> []; _Alloc = std::allocator<std::shared_ptr<int []> []>; _Args = {long unsigned int&}]’
/usr/include/c++/10/bits/shared_ptr.h:875:39:   required from ‘std::shared_ptr<_Tp> std::make_shared(_Args&& ...) [with _Tp = std::shared_ptr<int []> []; _Args = {long unsigned int&}]’
test_shared.cpp:7:63:   required from here
/usr/include/c++/10/bits/stl_construct.h:96:17: error: no matching function for call to ‘std::shared_ptr<int []>::shared_ptr(long unsigned int&)’
   96 |     -> decltype(::new((void*)0) _Tp(std::declval<_Args>()...))
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

g++-10 -v output:

Using built-in specs.
COLLECT_GCC=g++-10
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/10/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 10.2.0-5ubuntu1~20.04' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-10-WJNXnb/gcc-10-10.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-10-WJNXnb/gcc-10-10.2.0/debian/tmp-gcn/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) 

And from clang:

In file included from test_shared.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/memory:64:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/allocator.h:46:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/c++allocator.h:33:
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/ext/new_allocator.h:150:23: error: no matching constructor for initialization of 'std::shared_ptr<int []>'
        { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
                             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/alloc_traits.h:512:8: note: in instantiation of function template specialization '__gnu_cxx::new_allocator<std::shared_ptr<int []> []>::construct<std::shared_ptr<int []>, unsigned long &>' requested here
          __a.construct(__p, std::forward<_Args>(__args)...);
              ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr_base.h:551:30: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<std::shared_ptr<int []> []> >::construct<std::shared_ptr<int []>, unsigned long &>' requested here
          allocator_traits<_Alloc>::construct(__a, _M_ptr(),
                                    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr_base.h:683:6: note: in instantiation of function template specialization 'std::_Sp_counted_ptr_inplace<std::shared_ptr<int []>, std::allocator<std::shared_ptr<int []> []>, __gnu_cxx::_S_atomic>::_Sp_counted_ptr_inplace<unsigned long &>' requested here
            _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
            ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr_base.h:1371:14: note: in instantiation of function template specialization 'std::__shared_count<__gnu_cxx::_S_atomic>::__shared_count<std::shared_ptr<int []>, std::allocator<std::shared_ptr<int []> []>, unsigned long &>' requested here
        : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...)
                    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:408:4: note: in instantiation of function template specialization 'std::__shared_ptr<std::shared_ptr<int []> [], __gnu_cxx::_S_atomic>::__shared_ptr<std::allocator<std::shared_ptr<int []> []>, unsigned long &>' requested here
        : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...)
          ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:859:14: note: in instantiation of function template specialization 'std::shared_ptr<std::shared_ptr<int []> []>::shared_ptr<std::allocator<std::shared_ptr<int []> []>, unsigned long &>' requested here
      return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
             ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:875:19: note: in instantiation of function template specialization 'std::allocate_shared<std::shared_ptr<int []> [], std::allocator<std::shared_ptr<int []> []>, unsigned long &>' requested here
      return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
                  ^
test_shared.cpp:7:21: note: in instantiation of function template specialization 'std::make_shared<std::shared_ptr<int []> [], unsigned long &>' requested here
        auto layout = std::make_shared<std::shared_ptr<cell_t[]>[]>(y);
                           ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:149:7: note: candidate constructor not viable: no known conversion from 'unsigned long' to 'const std::shared_ptr<int []>' for 1st argument
      shared_ptr(const shared_ptr&) noexcept = default; ///< Copy constructor
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:303:7: note: candidate constructor not viable: no known conversion from 'unsigned long' to 'std::shared_ptr<int []>' for 1st argument
      shared_ptr(shared_ptr&& __r) noexcept
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:356:17: note: candidate constructor not viable: no known conversion from 'unsigned long' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
      constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
                ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:159:2: note: candidate template ignored: could not match '_Yp *' against 'unsigned long'
        shared_ptr(_Yp* __p) : __shared_ptr<_Tp>(__p) { }
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:295:2: note: candidate template ignored: could not match 'shared_ptr<type-parameter-0-0>' against 'unsigned long'
        shared_ptr(const shared_ptr<_Yp>& __r) noexcept
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:312:2: note: candidate template ignored: could not match 'shared_ptr<type-parameter-0-0>' against 'unsigned long'
        shared_ptr(shared_ptr<_Yp>&& __r) noexcept
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:324:11: note: candidate template ignored: could not match 'weak_ptr<type-parameter-0-0>' against 'unsigned long'
        explicit shared_ptr(const weak_ptr<_Yp>& __r)
                 ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:331:2: note: candidate template ignored: could not match 'auto_ptr<type-parameter-0-0>' against 'unsigned long'
        shared_ptr(auto_ptr<_Yp>&& __r);
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:339:2: note: candidate template ignored: could not match 'unique_ptr<type-parameter-0-0, type-parameter-0-1>' against 'unsigned long'
        shared_ptr(unique_ptr<_Yp, _Del>&& __r)
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:348:2: note: candidate template ignored: could not match 'unique_ptr<type-parameter-0-0, type-parameter-0-1>' against 'unsigned long'
        shared_ptr(unique_ptr<_Yp, _Del>&& __r)
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:407:2: note: candidate template ignored: could not match '_Sp_alloc_shared_tag<type-parameter-0-0>' against 'unsigned long'
        shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:147:17: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
      constexpr shared_ptr() noexcept : __shared_ptr<_Tp>() { }
                ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:176:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
        shared_ptr(_Yp* __p, _Deleter __d)
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:193:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
        shared_ptr(nullptr_t __p, _Deleter __d)
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:256:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
        shared_ptr(const shared_ptr<_Yp>& __r, element_type* __p) noexcept
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:416:7: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
      shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t)
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:213:2: note: candidate constructor template not viable: requires 3 arguments, but 1 was provided
        shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/shared_ptr.h:232:2: note: candidate constructor template not viable: requires 3 arguments, but 1 was provided
        shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
        ^
1 error generated.

clang -v output:

clang version 10.0.0-4ubuntu1 
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/10
Candidate multilib: .;@m64
Selected multilib: .;@m64

Original code, if it helps,

class gridmap {
    private:
        std::shared_ptr<std::shared_ptr<cell_t[]>[]> layout;
        std::size_t w;
        std::size_t h;
    public:
        gridmap(std::size_t y, std::size_t x) {
            layout = std::make_shared<std::shared_ptr<cell_t[]>[]>(y);
            for (std::size_t i = 0; i < y; i += 1) {
                layout[i] = std::make_shared<cell_t[]>(x);
            }
            h = y;
            w = x;
        }
        std::shared_ptr<cell_t[]>& operator[](size_t r) {
            return layout[r];
        }
};

Solution

  • For your first question "What is this error about?":

    GCC libstdc++ and Clang libc++ has no support for "Extending std::make_shared() to support arrays " which introduced in c++20 yet. So these compilers will try to use template< class T, class... Args > shared_ptr<T> make_shared( Args&&... args );, which trying to forward your arguments (in this case, a cell_t = std::size_t) to construct a std::shared_ptr<cell_t[]>[]. It cannot be done, so they complain about it.

    You can check compiler compatibility here: Compiler support for C++20