为什么在模板替换过程中此包装分配器的构造函数采用错误的类型(完美的转发ctor?)>

问题描述 投票:3回答:1

[对于某些情况,我正在编写一个分配器,该分配器将基本分配器作为模板类型,除了将allocate()deallocate()调用转发给基础分配器成员外,什么也不做。使用此自定义分配器创建std::vector可以正常工作。 我尝试编写一个make_shared包装器,默认情况下将使用dummy_allocator<T, std::allocator<T>>,但未成功。

这是一个可重现的示例:
#include <memory>
namespace test {

template<typename T, typename base_allocator=std::allocator<T>>
class dummy_allocator {
public:
    typedef typename std::allocator_traits<base_allocator>::size_type size_type;
    typedef typename std::allocator_traits<base_allocator>::difference_type difference_type;
    typedef typename std::allocator_traits<base_allocator>::pointer pointer;
    typedef typename std::allocator_traits<base_allocator>::const_pointer const_pointer;
    typedef typename std::allocator_traits<base_allocator>::value_type value_type;

    template<class U>
    struct rebind {
        typedef dummy_allocator<U,
                typename std::allocator_traits<base_allocator>::template rebind_alloc<U>> other;
    };

    template<typename... Args>
    dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}


    dummy_allocator(const dummy_allocator& a) = default;

    [[nodiscard]] T *allocate(std::size_t n) {
        T *p = alloc.allocate(n);
        return p;
    }

    void deallocate(T *p, std::size_t size) noexcept {
        alloc.deallocate(p, size);
    }

private:
    base_allocator alloc;
};

/// Allocate using a wrapped version of passed in allocator
template <typename T, typename Alloc, typename... Args>
std::shared_ptr<T> allocate_shared(const Alloc& alloc, Args&&... args) {
    auto dummy_alloc = dummy_allocator<T, Alloc>(alloc);
    return std::allocate_shared<T>(dummy_alloc, std::forward<Args>(args)...);
}

/// Create a shared pointer from a default stl allocator wrapped in profile allocator.
template <typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args) {
    return test::allocate_shared<T>(std::allocator<T>(), std::forward<Args>(args)...);
}

} // namespace test

int main() {
    auto ptr = test::make_shared<double>();
    return 0;
}

当我运行上面的代码时,编译器生成了一些奇怪的模板替换失败错误:

/usr/include/c++/10.1.0/bits/shared_ptr_base.h:679:43:   required from ‘std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = double; _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10.1.0/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 = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; _Tp = double; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10.1.0/bits/shared_ptr.h:408:59:   required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; _Tp = double]’
/usr/include/c++/10.1.0/bits/shared_ptr.h:859:14:   required from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = double; _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}]’
/home/ray/home/testing/src/alloc.cpp:44:35:   required from ‘std::shared_ptr<_Tp> test::allocate_shared(const Alloc&, Args&& ...) [with T = double; Alloc = std::allocator<double>; Args = {}]’
/home/ray/home/testing/src/alloc.cpp:50:36:   required from ‘std::shared_ptr<_Tp> test::make_shared(Args&& ...) [with T = double; Args = {}]’
/home/ray/home/testing/src/alloc.cpp:86:46:   required from here
/home/ray/home/testing/src/alloc.cpp:25:82: error: no matching function for call to ‘std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >::allocator(const test::dummy_allocator<double, std::allocator<double> >&)’
   25 |     dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
      |                                                                                  ^
In file included from /usr/include/c++/10.1.0/list:61,
                 from /home/ray/home/testing/src/alloc.cpp:3:
/usr/include/c++/10.1.0/bits/allocator.h:157:2: note: candidate: ‘template<class _Tp1> constexpr std::allocator< <template-parameter-1-1> >::allocator(const std::allocator<_Tp1>&) [with _Tp1 = _Tp1; _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
  157 |  allocator(const allocator<_Tp1>&) _GLIBCXX_NOTHROW { }
      |  ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:157:2: note:   template argument deduction/substitution failed:
/home/ray/home/testing/src/alloc.cpp:25:82: note:   ‘const test::dummy_allocator<double, std::allocator<double> >’ is not derived from ‘const std::allocator<_Up>’
   25 |     dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
      |                                                                                  ^
In file included from /usr/include/c++/10.1.0/list:61,
                 from /home/ray/home/testing/src/alloc.cpp:3:
/usr/include/c++/10.1.0/bits/allocator.h:147:7: note: candidate: ‘constexpr std::allocator< <template-parameter-1-1> >::allocator(const std::allocator< <template-parameter-1-1> >&) [with _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
  147 |       allocator(const allocator& __a) _GLIBCXX_NOTHROW
      |       ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:147:34: note:   no known conversion for argument 1 from ‘const test::dummy_allocator<double, std::allocator<double> >’ to ‘const std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >&’
  147 |       allocator(const allocator& __a) _GLIBCXX_NOTHROW
      |                 ~~~~~~~~~~~~~~~~~^~~
/usr/include/c++/10.1.0/bits/allocator.h:144:7: note: candidate: ‘constexpr std::allocator< <template-parameter-1-1> >::allocator() [with _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
  144 |       allocator() _GLIBCXX_NOTHROW { }
      |       ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:144:7: note:   candidate expects 0 arguments, 1 provided
... (The above error basically is repeated 2 more times)

尤其是此错误似乎代表了所发生的问题:

error: no matching function for call to ‘std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >::allocator(const test::dummy_allocator<double, std::allocator<double> >&)’

基本上说“在dummy_allocator的构造函数中,您不能将dummy_allocator传递给std::allocator的构造函数”。但是我没有那样做。在allocate_shared中,我将std::allocator传递到dummy_allocator

我真的很伤脑筋,看不懂编译器错误,但对我做错的事情没有任何结论。任何帮助将不胜感激!

编辑:我想我有一种预感,无论魔术shared_ptr在做什么,它都在尝试做我的dummy_allocator的副本构造函数,而完美的转发是捕获副本构造而不是实际的副本构造函数。 但是,我有no

想法,因为它是可变参数模板,因此无法解决此问题,并且我不能在完美的转发构造函数中使用std::is_same<Args, dummy_allocator>作为require子句。

因此,正如Daniel Langr所指出的那样,仅复制构造dummy_allocator将会失败。我通过以下require子句解决了该问题:

...
    template <typename T1, typename ...TV>
    struct is_dummy : std::is_same<typename std::decay<T1>::type, dummy_allocator<T, base_allocator>>{
    };

    template<typename... Args>
    requires (!is_dummy<Args...>::value)
    dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}

    dummy_allocator() = default;
    dummy_allocator(const dummy_allocator& a) = default;
...

但是,这还不足以解决shared_ptr问题,因为错误会更长一些... *

[在某些情况下,我正在编写一个分配器,该分配器将基本分配器作为模板类型,除了将allocate()和deallocate()调用转发给基础分配器成员之外,什么也不做。 ...

c++ templates memory-management c++20 perfect-forwarding
1个回答
-1
投票

只需删除可变参数的构造函数并添加这两个:

© www.soinside.com 2019 - 2024. All rights reserved.