具有代理迭代器/引用和auto的容器

gnz*_*lbg 7 c++ iterator generic-programming c++11 c++14

我执行与代理迭代器/引用类型的容器类似,以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");
Run Code Online (Sandbox Code Playgroud)
  • 有没有办法实现代理类型,以便静态断言传递?

  • 在实现这样的容器时,是否有关于如何处理此问题的指导原则?

也许通过使用转换运算符来auto/ auto&/ auto&&/ const auto...

编辑:重新编写示例以使其更清晰.感谢@LucDanton在下面的评论.

Seb*_*edl 5

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

已经有一些对operator auto-style 事物感兴趣的请求(基本上,“当将我推导为一种类型时,请使用这种类型”),但据我所知,他们甚至都没有提出正式提案。

另一个问题是vector<bool>意想不到的,因为它是使用代理的唯一实例。vector还有其他初步提案要求vector<bool>弃用并最终恢复为非特殊类,并bitvector引入特殊用途类来取代它。

  • 不。代理并不完全透明,而“auto”使它们变得比以前更不透明。这是语言上的弱点。 (2认同)

Tem*_*Rex 3

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

相关差异在于,嵌套类型reference和在一般情况下const_referencetypedefforT&和,而对于的代理类值类型T const& reference boolvector<bool>

访问向量元素时,记住向量对象的常量性决定是否返回areference或也很重要。此外,将删除参考限定符,而将保留它们。const_referenceoperator[]autodecltype

bool让我们看一下/的非常量/常量向量int,并使用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";  
}
Run Code Online (Sandbox Code Playgroud)

实例

请注意,对于非常量vector<bool>,使用autoonoperator[]将为您提供一个没有值语义的引用代理。使用 aconst vector<bool>可以避免这种情况。我不知道如何以其他方式解决这个问题。

行为上是等效的,但在 内部auto const&有一个is_convertible而不是。我认为这是最好的。is_samestatic_assert

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