我正在实现一个带有代理迭代器/引用类型类似的容器到std::vector<bool>
并碰撞到以下问题,我将继续以std::vector<bool>
为例(这个问题与std::vector<bool>
无关!):
#include <vector>
#include <type_traits>
int main() {
using namespace std;
vector<bool> vec = {true, false, true, false};
auto value = vec[2]; // expect: "vector<bool>::value_type"
const auto& reference = vec[2]; // expect: "vector<bool>::const_reference"
static_assert(is_same<decltype(value), vector<bool>::value_type>::value,
"fails: type is vector<bool>::reference!");
static_assert(is_same<decltype(reference),
vector<bool>::const_reference>::value,
"fails: type is const vector<bool>::reference&!");
/// Consequence:
auto other_value = value;
other_value = false;
assert(vec[2] == true && "fails: assignment modified the vector");
有没有办法实现代理类型,以便静态断言都通过?
在实施此类容器时是否有关于如何处理此问题的指导原则?
也许使用转化运算符auto
/ auto&
/ auto&&
/ const auto...
?
编辑:重新设计示例以使其更清晰。感谢@LucDanton在下面的评论。
答案 0 :(得分:3)
众所周知,vector<bool>
与主要模板vector<T>
相比具有非通用接口。
相关的区别在于,嵌套类型reference
和const_reference
在{1}} typedef
T&
和T const&
一般情况下都是reference
{> 1}的强>代理类 bool
和值类型 vector<bool>
。
访问向量元素时,同样重要的是要记住向量对象的常量确定{{1}是否返回reference
或const_reference
}。此外,operator[]
将删除参考限定符,而auto
将保留参考限定符。
让我们看一下decltype
/ bool
的非const / const向量,并使用int
,auto
和decltype(auto)
(普通auto const&
将导致代理的实时问题)。您会收到以下行为:
auto&
请注意,对于非const #include <vector>
#include <type_traits>
#include <typeinfo>
#include <iostream>
#include <ios>
int main() {
using namespace std;
vector<bool> vb = { true, false, true, false };
vector<int > vi = { 1, 0, 1, 0 };
auto vb2 = vb[2]; // vector<bool>::reference != bool
auto vi2 = vi[2]; // int
decltype(auto) rvb2 = vb[2]; // vector<bool>::reference
decltype(auto) rvi2 = vi[2]; // int&
auto const& crvb2 = vb[2]; // vector<bool>::reference const& != bool const&
auto const& crvi2 = vi[2]; // int const&
auto ovb2 = vb2;
ovb2 = false; // OOPS ovb2 has reference semantics
cout << boolalpha << (vb[2] == true) << "\n";
auto ovi2 = vi2;
ovi2 = 0; // OK, ovi2 has value semantics
cout << boolalpha << (vi[2] == 1) << "\n";
static_assert(is_convertible<decltype(vb2), vector<bool>::value_type>::value, "");
static_assert(is_same <decltype(vi2), vector<int >::value_type>::value, "");
static_assert(is_same <decltype(rvb2), vector<bool>::reference>::value, "");
static_assert(is_same <decltype(rvi2), vector<int >::reference>::value, "");
static_assert(is_convertible<decltype(crvb2), vector<bool>::const_reference>::value, "");
static_assert(is_same <decltype(crvi2), vector<int >::const_reference>::value, "");
vector<bool> const cvb = { true, false, true, false };
vector<int > const cvi = { 1, 0, 1, 0 };
auto cvb2 = cvb[2]; // vector<bool>::const_reference == bool
auto cvi2 = cvi[2]; // int
decltype(auto) rcvb2 = cvb[2]; // vector<bool>::const_reference == bool
decltype(auto) rcvi2 = cvi[2]; // int const&
auto const& crcvb2 = cvb[2]; // vector<bool>::reference const& != bool const&
auto const& crcvi2 = cvi[2]; // int const&
static_assert(is_same <decltype(cvb2), vector<bool>::value_type>::value, "");
static_assert(is_same <decltype(cvi2), vector<int >::value_type>::value, "");
static_assert(is_same <decltype(rcvb2), vector<bool>::const_reference>::value, "");
static_assert(is_same <decltype(rcvi2), vector<int >::const_reference>::value, "");
static_assert(is_convertible<decltype(crcvb2), vector<bool>::const_reference>::value, "");
static_assert(is_same <decltype(crcvi2), vector<int >::const_reference>::value, "");
auto ocvb2 = cvb2;
ocvb2 = false; // OK, ocvb2 has value semantics
cout << boolalpha << (cvb[2] == true) << "\n";
auto ocvi2 = cvi2;
ocvi2 = 0; // OK, ocvi2 has value semantics
cout << boolalpha << (cvi[2] == 1) << "\n";
}
,在vector<bool>
上使用auto
将为您提供一个没有值语义的引用代理。使用operator[]
可以避免这种情况。我不知道如何以其他任何方式解决这个问题。
const vector<bool>
在行为上是等效的,但在auto const&
内有is_convertible
而不是is_same
。我认为这是最好的。
请注意,对于代理容器上的一般迭代和STL算法,事情并不是那么黯淡。请参阅Hinnant's column on this。
答案 1 :(得分:3)
代理与auto
互动性不佳,正是因为auto
揭示了应该隐藏的类型。
对operator auto
风格的事情有一半的要求(基本上,“当我将其作为一种类型推断时,请使用此类型”),但AFAIK甚至没有将其作为正式提案。
另一个问题是vector<bool>
是意外的,因为它是使用代理的{em>仅实例化vector
。还有其他初步提案要求弃用vector<bool>
并最终恢复为非特殊,并引入了一个特殊目的bitvector
类来取代它。