c++ - 为什么这个包装分配器的构造函数在模板替换期间采用错误的类型(完美的转发 ctor)?
问题描述
allocate()
在某些情况下,我正在编写一个分配器,它将基本分配器作为模板类型,除了转发和deallocate()
调用底层分配器成员之外什么都不做。std::vector
使用此自定义分配器创建s 可以正常工作。我尝试编写一个默认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
,您不能将 a 传递dummy_allocator
给std::allocator
' 的构造函数”。但我不这样做。在allocate_shared
中,我正在std::allocator
传入dummy_allocator
。
我真的在阅读编译器错误时挠了挠头,但对我做错了什么没有得出任何结论。任何帮助将不胜感激!
编辑:我想我有一种预感,无论 shared_ptr 在下面做什么魔法,它都在尝试做我的 dummy_allocator 的复制构造函数,而完美的转发是捕获复制构造而不是实际的复制构造函数。但是,我不知道如何解决这个问题,因为它是一个可变参数模板,我不能std::is_same<Args, dummy_allocator>
在完美的转发构造函数中用作 requires 子句。
因此,正如 Daniel Langr 所指出的,仅复制构造 dummy_allocator 将失败。我用以下 requires 子句解决了这个问题:
...
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
问题,错误会更长一些……*
所以这是你得到的错误:
#include <memory>
namespace test {
template<typename T, typename base_allocator=std::allocator<T>>
class dummy_allocator {
public:
/// Necessary for allocators, propagate exactly what the base_allocator
/// wants.
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 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;
[[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>
auto 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>
auto make_shared(Args&&... args) {
return test::allocate_shared<T>(std::allocator<T>(), std::forward<Args>(args)...);
}
} // namespace test
int main() {
// This will fail
auto ptr = test::make_shared<double>();
// This will now work
auto dummy_alloc = test::dummy_allocator<int, std::allocator<int>>();
auto dummy_alloc2 = test::dummy_allocator<int, std::allocator<int>>(dummy_alloc);
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/test/src/alloc.cpp:53:35: required from ‘auto test::allocate_shared(const Alloc&, Args&& ...) [with T = double; Alloc = std::allocator<double>; Args = {}]’
/home/ray/home/test/src/alloc.cpp:59:36: required from ‘auto test::make_shared(Args&& ...) [with T = double; Args = {}]’
/home/ray/home/test/src/alloc.cpp:95:46: required from here
/home/ray/home/test/src/alloc.cpp:31: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> >&)’
31 | 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/test/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/test/src/alloc.cpp:31:82: note: ‘const test::dummy_allocator<double, std::allocator<double> >’ is not derived from ‘const std::allocator<_Up>’
31 | 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/test/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
解决方案
只需删除可变参数构造函数并添加这两个:
dummy_allocator(const base_allocator& a) : alloc(a)
{}
template<class U, class Alloc>
dummy_allocator(const dummy_allocator<U, Alloc>& a) : alloc(a.alloc)
{}
如果我想将参数转发给需要参数的分配器,这并不能解决问题:(
然后你可以使用std::is_constructible
:
template<class... Args>
dummy_allocator(Args&&... args)
requires(std::is_constructible_v<base_allocator, Args...>)
: alloc(std::forward<Args>(args)...)
{}
template<class U, class Alloc>
dummy_allocator(const dummy_allocator<U, Alloc>& a) : alloc(a.alloc)
{}
请注意,template<class U, class Alloc> dummy_allocator
在这两种情况下都需要构造函数,因为对于任何重新绑定的分配器类型都dummy_allocator a(b)
应该是格式良好的。b
推荐阅读
- c# - App Config 中的多组 ConnectionStrings
- jenkins - 如何使用 groovy 获取另一个作业的主分支的成功构建号
- java - JSON Array request cannot be applied 关于构造函数的错误
- javascript - 在 Jupyter notebook 中用 python 包装 JS 函数的解决方法
- javascript - 如何遍历多个 URL 以获取 Google Apps 脚本中的 JSON 响应?
- ios - Firebase Analytics DebugView 收集的事件数据不完整
- powershell - Google Calendar API 的 PowerShell Invoke-RestMethod 仅返回 1 个事件
- javascript - 如何使用javascript以数组格式显示一组地图
- java - 默认的 micronaut 应用程序未运行
- xamarin - 当我从 xamarin 中的同步领域数据库中删除项目时出现空引用异常