9

我正在实现一个具有类似于以下问题的代理迭代器/引用类型的容器,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 在下面的评论。

4

2 回答 2

4

代理并auto不能很好地交互,正是因为auto揭示了应该隐藏的类型的事情。

有一些对operator auto-style 事物感兴趣的请求(基本上,“当将我推断为一种类型时,请使用这种类型”),但 AFAIK 甚至都没有将其提交给官方提案。

另一个问题是vector<bool>出乎意料的,因为它是唯一vector使用代理的实例。还有其他初步建议要求vector<bool>弃用并最终恢复为非特殊,bitvector引入一个特殊用途的类来代替它。

于 2013-11-22T12:26:06.090 回答
3

众所周知,与主模板相比,vector<bool>它具有非通用接口vector<T>

相关的区别在于嵌套类型referenceandconst_referencetypedefforT&T const&在一般情况下,以及代理类 reference值类型 boolfor vector<bool>

在访问向量元素时,记住向量对象的常量性决定了 areferenceconst_reference是否由 . 返回也很重要operator[]。此外,auto将删除参考限定符,而decltype将保留那些。

让我们看一下 / 的非 const / const 向量boolint并使用auto, decltype(auto)and auto const&(显然auto&会导致代理的实时问题)。您会得到以下行为:

#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>,使用autoonoperator[]将为您提供没有值语义的引用代理。使用 aconst vector<bool>将避免这种情况。我看不出如何以任何其他方式解决这个问题。

Theauto const&在行为上是等效的,is_convertibleis_samestatic_assert. 我认为这是最好的。

请注意,对于代理容器上的一般迭代和 STL 算法,情况并没有那么暗淡。请参阅Hinnant 的专栏

于 2013-11-22T09:57:22.620 回答