代码之家  ›  专栏  ›  技术社区  ›  Resigned June 2023 cmaynard

指向成员的指针的语法sugar适用于数组,但不适用于std::vector

  •  3
  • Resigned June 2023 cmaynard  · 技术社区  · 8 年前

    为了理解C++,我写了一些示例代码(如下) 指向成员的指针 特色然而,我遇到了一个奇怪的问题,语法

    (*it).*attribute
    

    it->*attribute
    

    不接受,有错误

    左手操作数到 ->* 必须是指向与右侧操作数兼容的类的指针,但为 std::__1::__wrap_iter<Bowl *>

    但是,如果我取消注释 Bowl bowls[3] = 并注释掉 std::vector<Bowl> bowls = ,即从使用 std::vector 若要使用基元数组,则两种语法都能正确工作。

    C++ reference

    表达式 E1->*E2 完全等同于 (*E1).*E2

    因此,错误似乎与以下事实有关:数组是内置类型,但 标准::矢量 不是。后来在那一节中,我发现

    在针对用户定义运算符的重载解析中,对于类型D、B、R的每个组合,其中类类型B要么是与D相同的类,要么是D的明确且可访问的基类,而R要么是对象类型,要么是函数类型,以下函数签名参与重载解析:

    R& operator->*(D*, R B::*);
    

    标准::矢量 operator->* 我很困惑。为什么我在一种语法上出现这个错误,而在另一种语法上没有,而且只有在使用 标准::矢量 而不是原始数组?

    #include <iostream>
    #include <iterator>
    #include <vector>
    
    class Bowl
    {
    public:
      unsigned int apples;
      unsigned int oranges;
      unsigned int bananas;
    
      Bowl(unsigned int apples, unsigned int oranges, unsigned int bananas)
        : apples(apples), oranges(oranges), bananas(bananas)
      {
        // nothing to do here
      }
    };
    
    template <typename TClass, typename TIterator, typename TResult>
    TResult sum_attribute(TIterator begin, TIterator end, TResult TClass::*attribute)
    {
      TResult sum = 0;
      for (TIterator it = begin; it != end; ++it)
      {
        sum += (*it).*attribute;
        sum += it->*attribute;
      }
      return sum;
    }
    
    int main()
    {
      std::vector<Bowl> bowls =
      // Bowl bowls[3] =
        {
          {1, 2, 3},
          {4, 5, 6},
          {7, 8, 9},
        };
    
      int num_apples = sum_attribute(std::begin(bowls), std::end(bowls), &Bowl::apples);
      int num_oranges = sum_attribute(std::begin(bowls), std::end(bowls), &Bowl::oranges);
      int num_bananas = sum_attribute(std::begin(bowls), std::end(bowls), &Bowl::bananas);
    
      std::cout << "We have " << num_apples << " apples, " << num_oranges << " oranges, and " <<
        num_bananas << " bananas. Now wasn't that fun?" << std::endl;
    }
    
    1 回复  |  直到 8 年前
        1
  •  3
  •   M.M    8 年前

    * -> 操作员。

    如果你有一个类类型的迭代器,你写

    (*itr).field
    

    C++将其解释为

    itr.operator*().field
    

    同样,如果你写

    itr->field
    

    itr.operator->().field
    

    注意,它们调用了不同的重载运算符。第一个呼叫 operator* ,第二次通话 operator-> . 这与内置类型不同;正如您所注意到的,对于内置类型,语法

    base->field
    

    只是

    (*base).field
    

    当你谈到

    (*itr).*memberPtr
    

    itr->*memberPtr
    

    在第一种情况下,C++处理 (*itr).*memberPtr 意味着

    itr.operator*().*memberPtr
    

    请注意,这意味着 .* .* 操作人员另一方面,如果你写

    itr->*成员PTR
    

    C++将其视为对

    itr.operator->*(memberPtr)
    

    并报告一个错误,因为迭代器类型不需要实现,而且很少实现 operator ->* . (事实上,我已经看到了 操作员->*

    基元类型处理 base->*memberPtr (*base).*memberPtr 这是不相关的,这与基元类型处理 base->field (*base).field ; 编译器不会自动生成 操作员* 从…起 操作员->

    还有一个单独的问题,关于为什么不需要迭代器来做这件事,不幸的是,我没有一个好的答案。我的猜测是,这是一个非常罕见的案例,没有人想过将其纳入标准,但我不确定。

    至于为什么这适用于基本数组,但不适用于 std::vector ,您正在使用原始指针的基本数组,它支持 ->* 操作人员的迭代器 标准::矢量 不需要是原始指针,如果它们是类类型的对象,上述推理解释了为什么不应该期望它们支持 操作员->* .