为什么这个分配器不能与 std::allocate_shared 一起工作?奇怪的模板替换错误
Why does this allocator not work with `std::allocate_shared`? Bizarre template substitution errors
foo_allocator
是 stl 容器的工作分配器。它覆盖了 base
分配器类型并将 allocate()
、deallocate()
、operator==
、operator!=
等转发到基础。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
template <typename T>
class bar_allocator : public std::allocator<T> {};
template <typename T, typename base=bar_allocator<T>>
class foo_allocator {
public:
typedef typename std::allocator_traits<base>::value_type value_type;
template <typename U, typename A> friend class foo_allocator;
template<class U>
struct rebind {
typedef foo_allocator<U,
typename std::allocator_traits<base>::template rebind_alloc<U>> other;
};
// Construct a dummy allocator from another dummy allocator with the same base_allocator but with different type.
template <typename U>
foo_allocator(
const foo_allocator<U,
typename std::allocator_traits<base>::template rebind_alloc<U>>& other) noexcept :
alloc(other.alloc) {}
foo_allocator() = default;
template<typename... Args>
foo_allocator(Args &&... args) requires (std::is_constructible_v<base, Args...>) : alloc(std::forward<Args>(args)...) {}
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 alloc;
};
template <typename T, typename U, typename base_allocator>
inline bool operator == (const foo_allocator<T, base_allocator>& a,
const foo_allocator<U, typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& b)
{
return a.alloc == b.alloc;
}
template <typename T, typename U, typename base_allocator>
inline bool operator != (const foo_allocator<T, base_allocator>& a,
const foo_allocator<U, typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& b)
{
return a.alloc != b.alloc;
}
int main()
{
// Works fine
std::vector<int, foo_allocator<int>> v;
for (int i = 0; i < 100; i++)
v.push_back(i);
// Breaks!
// foo_allocator<int> foo;
// std::shared_ptr<int> ptr2 = std::allocate_shared<int>(foo);
}
但是,它不适用于 allocate_shared
。如果您尝试使用此分配器 allocate_shared
,您会收到以下编译器错误:
In file included from /usr/local/include/c++/9.2.0/bits/shared_ptr.h:52,
from /usr/local/include/c++/9.2.0/memory:81,
from main.cpp:4:
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h: In instantiation of 'std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = int; _Alloc = foo_allocator<int>; _Args = {}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]':
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:1344:71: required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = foo_allocator<int>; _Args = {}; _Tp = int; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
/usr/local/include/c++/9.2.0/bits/shared_ptr.h:359:59: required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = foo_allocator<int>; _Args = {}; _Tp = int]'
/usr/local/include/c++/9.2.0/bits/shared_ptr.h:701:14: required from 'std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = int; _Alloc = foo_allocator<int>; _Args = {}]'
main.cpp:71:62: required from here
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:676:43: error: no matching function for call to 'foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(const foo_allocator<int>&)'
676 | typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
| ^~~~
main.cpp:32:5: note: candidate: 'foo_allocator<T, base>::foo_allocator(Args&& ...) requires is_constructible_v<base, Args ...> [with Args = {const foo_allocator<int, bar_allocator<int> >&}; T = std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>; base = std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> >]'
32 | foo_allocator(Args &&... args) requires (std::is_constructible_v<base, Args...>) : alloc(std::forward<Args>(args)...) {}
| ^~~~~~~~~~~~~
main.cpp:32:5: note: constraints not satisfied
main.cpp:32:5: note: 'is_constructible_v<base, Args ...>' evaluated to false
main.cpp:29:5: note: candidate: 'constexpr foo_allocator<T, base>::foo_allocator() [with T = std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>; base = std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> >]'
29 | foo_allocator() = default;
| ^~~~~~~~~~~~~
main.cpp:29:5: note: candidate expects 0 arguments, 1 provided
main.cpp:24:5: note: candidate: 'template<class U> foo_allocator<T, base>::foo_allocator(const foo_allocator<U, typename std::allocator_traits<_Alloc>::rebind_alloc<U> >&)'
24 | foo_allocator(
| ^~~~~~~~~~~~~
main.cpp:24:5: note: template argument deduction/substitution failed:
In file included from /usr/local/include/c++/9.2.0/bits/shared_ptr.h:52,
from /usr/local/include/c++/9.2.0/memory:81,
from main.cpp:4:
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:676:43: note: mismatched types 'std::allocator<_CharT>' and 'bar_allocator<int>'
676 | typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
| ^~~~
main.cpp:10:7: note: candidate: 'constexpr foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(const foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&)'
10 | class foo_allocator {
| ^~~~~~~~~~~~~
main.cpp:10:7: note: no known conversion for argument 1 from 'const foo_allocator<int>' to 'const foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&'
main.cpp:10:7: note: candidate: 'constexpr foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&&)'
main.cpp:10:7: note: no known conversion for argument 1 from 'const foo_allocator<int>' to 'foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&&'
我可以从错误消息中收集到什么
std::shared_ptr
进行了一些重新绑定以创建一个结构来覆盖我们正在尝试分配的 int
。特别是,它尝试分配类型 std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>
,我猜它有 int
,但也有其他字段用于确保 shared_ptr
是线程安全的。
- 因此,我们有这个 非常丑陋的结构 ,它使用
foo_allocator
: 进行一些自引用
foo_allocator<
std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>,
std::allocator<
std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>
>
>
但是等等!为什么 std::allocator<>
在这里?我们从未指定 std::allocator<>
作为我们的基本分配器!应该是 bar_allocator
!
- 稍后查看错误日志,我们看到以下奇怪的行:
mismatched types 'std::allocator<_CharT>' and 'bar_allocator<int>'
除了std::allocator<>
又出现了,_CharT
是从哪里来的?这种模板类型一般不会出现在字符串中吗?
如有任何帮助,我们将不胜感激。我已经为此绞尽脑汁了一段时间,无法提出任何合理的解决办法。
感谢@Nicol Bolas 和@Igor Tandetnik,我才能够找出原因。正如他们所说,通过继承分配器,您还继承了重新绑定结构,它实际上是 重新绑定基 class。这不是我们想要的(我猜分配器不能很好地与继承一起工作),所以我们必须添加以下内容才能让它工作:
template <typename T>
class bar_allocator : public std::allocator<T> {
public:
bar_allocator() = default;
template <typename U>
bar_allocator(const bar_allocator<U>& other) : std::allocator<T>(other){
}
template<class U>
struct rebind {
typedef bar_allocator<U> other;
};
};
foo_allocator
是 stl 容器的工作分配器。它覆盖了 base
分配器类型并将 allocate()
、deallocate()
、operator==
、operator!=
等转发到基础。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
template <typename T>
class bar_allocator : public std::allocator<T> {};
template <typename T, typename base=bar_allocator<T>>
class foo_allocator {
public:
typedef typename std::allocator_traits<base>::value_type value_type;
template <typename U, typename A> friend class foo_allocator;
template<class U>
struct rebind {
typedef foo_allocator<U,
typename std::allocator_traits<base>::template rebind_alloc<U>> other;
};
// Construct a dummy allocator from another dummy allocator with the same base_allocator but with different type.
template <typename U>
foo_allocator(
const foo_allocator<U,
typename std::allocator_traits<base>::template rebind_alloc<U>>& other) noexcept :
alloc(other.alloc) {}
foo_allocator() = default;
template<typename... Args>
foo_allocator(Args &&... args) requires (std::is_constructible_v<base, Args...>) : alloc(std::forward<Args>(args)...) {}
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 alloc;
};
template <typename T, typename U, typename base_allocator>
inline bool operator == (const foo_allocator<T, base_allocator>& a,
const foo_allocator<U, typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& b)
{
return a.alloc == b.alloc;
}
template <typename T, typename U, typename base_allocator>
inline bool operator != (const foo_allocator<T, base_allocator>& a,
const foo_allocator<U, typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& b)
{
return a.alloc != b.alloc;
}
int main()
{
// Works fine
std::vector<int, foo_allocator<int>> v;
for (int i = 0; i < 100; i++)
v.push_back(i);
// Breaks!
// foo_allocator<int> foo;
// std::shared_ptr<int> ptr2 = std::allocate_shared<int>(foo);
}
但是,它不适用于 allocate_shared
。如果您尝试使用此分配器 allocate_shared
,您会收到以下编译器错误:
In file included from /usr/local/include/c++/9.2.0/bits/shared_ptr.h:52,
from /usr/local/include/c++/9.2.0/memory:81,
from main.cpp:4:
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h: In instantiation of 'std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = int; _Alloc = foo_allocator<int>; _Args = {}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]':
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:1344:71: required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = foo_allocator<int>; _Args = {}; _Tp = int; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
/usr/local/include/c++/9.2.0/bits/shared_ptr.h:359:59: required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = foo_allocator<int>; _Args = {}; _Tp = int]'
/usr/local/include/c++/9.2.0/bits/shared_ptr.h:701:14: required from 'std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = int; _Alloc = foo_allocator<int>; _Args = {}]'
main.cpp:71:62: required from here
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:676:43: error: no matching function for call to 'foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(const foo_allocator<int>&)'
676 | typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
| ^~~~
main.cpp:32:5: note: candidate: 'foo_allocator<T, base>::foo_allocator(Args&& ...) requires is_constructible_v<base, Args ...> [with Args = {const foo_allocator<int, bar_allocator<int> >&}; T = std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>; base = std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> >]'
32 | foo_allocator(Args &&... args) requires (std::is_constructible_v<base, Args...>) : alloc(std::forward<Args>(args)...) {}
| ^~~~~~~~~~~~~
main.cpp:32:5: note: constraints not satisfied
main.cpp:32:5: note: 'is_constructible_v<base, Args ...>' evaluated to false
main.cpp:29:5: note: candidate: 'constexpr foo_allocator<T, base>::foo_allocator() [with T = std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>; base = std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> >]'
29 | foo_allocator() = default;
| ^~~~~~~~~~~~~
main.cpp:29:5: note: candidate expects 0 arguments, 1 provided
main.cpp:24:5: note: candidate: 'template<class U> foo_allocator<T, base>::foo_allocator(const foo_allocator<U, typename std::allocator_traits<_Alloc>::rebind_alloc<U> >&)'
24 | foo_allocator(
| ^~~~~~~~~~~~~
main.cpp:24:5: note: template argument deduction/substitution failed:
In file included from /usr/local/include/c++/9.2.0/bits/shared_ptr.h:52,
from /usr/local/include/c++/9.2.0/memory:81,
from main.cpp:4:
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:676:43: note: mismatched types 'std::allocator<_CharT>' and 'bar_allocator<int>'
676 | typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
| ^~~~
main.cpp:10:7: note: candidate: 'constexpr foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(const foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&)'
10 | class foo_allocator {
| ^~~~~~~~~~~~~
main.cpp:10:7: note: no known conversion for argument 1 from 'const foo_allocator<int>' to 'const foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&'
main.cpp:10:7: note: candidate: 'constexpr foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&&)'
main.cpp:10:7: note: no known conversion for argument 1 from 'const foo_allocator<int>' to 'foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&&'
我可以从错误消息中收集到什么
std::shared_ptr
进行了一些重新绑定以创建一个结构来覆盖我们正在尝试分配的int
。特别是,它尝试分配类型std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>
,我猜它有int
,但也有其他字段用于确保shared_ptr
是线程安全的。- 因此,我们有这个 非常丑陋的结构 ,它使用
foo_allocator
: 进行一些自引用
foo_allocator<
std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>,
std::allocator<
std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>
>
>
但是等等!为什么 std::allocator<>
在这里?我们从未指定 std::allocator<>
作为我们的基本分配器!应该是 bar_allocator
!
- 稍后查看错误日志,我们看到以下奇怪的行:
mismatched types 'std::allocator<_CharT>' and 'bar_allocator<int>'
除了std::allocator<>
又出现了,_CharT
是从哪里来的?这种模板类型一般不会出现在字符串中吗?
如有任何帮助,我们将不胜感激。我已经为此绞尽脑汁了一段时间,无法提出任何合理的解决办法。
感谢@Nicol Bolas 和@Igor Tandetnik,我才能够找出原因。正如他们所说,通过继承分配器,您还继承了重新绑定结构,它实际上是 重新绑定基 class。这不是我们想要的(我猜分配器不能很好地与继承一起工作),所以我们必须添加以下内容才能让它工作:
template <typename T>
class bar_allocator : public std::allocator<T> {
public:
bar_allocator() = default;
template <typename U>
bar_allocator(const bar_allocator<U>& other) : std::allocator<T>(other){
}
template<class U>
struct rebind {
typedef bar_allocator<U> other;
};
};