为什么编译器允许使用与所用容器不同的值类型的分配器
Why do compilers allow an allocator of a different value type than the container used
似乎 C++ STL 容器要求提供的分配器类型 value_type 与 STL 容器的 value_type
相同
要求:allocator_-
type::value_type 是一样的
作为 X::value_type.
但是,以下使用字符串向量但带有双精度分配器的代码在 VS 2012 和 g++ 4.4.7 上运行良好。在 g++ 上,valgrind 也不会产生任何错误。
int main()
{
typedef vector<std::string, std::allocator<double> > StringList;
StringList s;
for(int i=0; i < 100; i++){
stringstream ss;
ss << i;
s.push_back(ss.str());
}
for(StringList::iterator it = s.begin(); it != s.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
我假设分配器正在内部反弹到容器 value_type 的分配器(尽管我可能错了)。
我的问题是我是否误解了 C++ 规范,实际上所有容器总是 "rebind" 提供的分配器使用他们想要的类型?或者这只是一种常见的做法但不能保证。
基本上我可以指望这个 "feature" 容器将始终采用我提供的任何分配器(任何类型)并使其适用于该容器的 value_type 吗?
当您编写自己的分配器时,原则上您可以在分配器中使用与您的类型特征不同的 value_type。
从 C++11 开始,提供了一个类型特征来检查类型 T 是否具有一个 allocator_type,传递的分配器可以转换成该类型特征。如果未提供此转换(或编译器未检查它),则您有未定义的行为。
在 MSVC 14.1 中,转换是通过简单的重新绑定完成的
template<class _Other>
struct rebind
{ // convert this type to allocator<_Other>
typedef allocator<_Other> other;
};
所以在 MSVC 中它确实进行了内部反弹,关于你的最后一个问题,我不会依赖这个实现保持这种方式,当然不会跨不同的编译器。另外我想知道为什么你会想要依赖它,而不是给出正确的类型特征?
如果您尝试使用 clang/libc++ 构建代码(添加适当的 includes
和 using namespace std;
,您将得到:
/Sources/LLVM/llvm/projects/libcxx/include/vector:474:5: error:
static_assert failed "Allocator::value_type must be same type as
value_type"
static_assert((is_same::value),
无论如何,标准对此非常明确(在 c++11/14/1z 中——但不是 c++03):
*Requires:* `allocator_type::value_type` is the same as `X::value_type`
因此,如果您尝试实例化 vector<std::string, std::allocator<double> >
,您会得到未定义的行为 - "seems to work fine" 是未定义行为的一个特别繁琐的版本。但实际上,它是 "seems to work fine for now"
似乎 C++ STL 容器要求提供的分配器类型 value_type 与 STL 容器的 value_type
相同要求:allocator_- type::value_type 是一样的 作为 X::value_type.
但是,以下使用字符串向量但带有双精度分配器的代码在 VS 2012 和 g++ 4.4.7 上运行良好。在 g++ 上,valgrind 也不会产生任何错误。
int main()
{
typedef vector<std::string, std::allocator<double> > StringList;
StringList s;
for(int i=0; i < 100; i++){
stringstream ss;
ss << i;
s.push_back(ss.str());
}
for(StringList::iterator it = s.begin(); it != s.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
我假设分配器正在内部反弹到容器 value_type 的分配器(尽管我可能错了)。
我的问题是我是否误解了 C++ 规范,实际上所有容器总是 "rebind" 提供的分配器使用他们想要的类型?或者这只是一种常见的做法但不能保证。
基本上我可以指望这个 "feature" 容器将始终采用我提供的任何分配器(任何类型)并使其适用于该容器的 value_type 吗?
当您编写自己的分配器时,原则上您可以在分配器中使用与您的类型特征不同的 value_type。
从 C++11 开始,提供了一个类型特征来检查类型 T 是否具有一个 allocator_type,传递的分配器可以转换成该类型特征。如果未提供此转换(或编译器未检查它),则您有未定义的行为。
在 MSVC 14.1 中,转换是通过简单的重新绑定完成的
template<class _Other>
struct rebind
{ // convert this type to allocator<_Other>
typedef allocator<_Other> other;
};
所以在 MSVC 中它确实进行了内部反弹,关于你的最后一个问题,我不会依赖这个实现保持这种方式,当然不会跨不同的编译器。另外我想知道为什么你会想要依赖它,而不是给出正确的类型特征?
如果您尝试使用 clang/libc++ 构建代码(添加适当的 includes
和 using namespace std;
,您将得到:
/Sources/LLVM/llvm/projects/libcxx/include/vector:474:5: error: static_assert failed "Allocator::value_type must be same type as value_type" static_assert((is_same::value),
无论如何,标准对此非常明确(在 c++11/14/1z 中——但不是 c++03):
*Requires:* `allocator_type::value_type` is the same as `X::value_type`
因此,如果您尝试实例化 vector<std::string, std::allocator<double> >
,您会得到未定义的行为 - "seems to work fine" 是未定义行为的一个特别繁琐的版本。但实际上,它是 "seems to work fine for now"