我写了一些示例代码(如下)来理解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
所以似乎错误与数组是内置类型但std::vector
不是这样的事实有关。在那一节的后面,我找到了
针对用户定义的运算符的重载解析,对于类型D,B,R的每种组合,其中类类型B与D是同一类,或者是D的明确且可访问的基类,并且R是对象或函数类型,以下函数签名参与重载解析:
R& operator->*(D*, R B::*);
但由于std::vector
未定义operator->*
,我感到非常困惑。为什么我在一种语法而不是另一种语法上得到此错误,并且只有在使用std::vector
而不是原始数组时?
#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;
}
答案 0 :(得分:3)
为了更好地理解这一点,我认为讨论与*
和->
运算符的相似之处很有帮助。
如果你有一个类类型的迭代器并且你写了
(*itr).field
C ++将其解释为
itr.operator*().field
同样,如果你写
itr->field
C ++将其解释为
itr.operator->().field
请注意,这些是调用不同的重载运算符。第一个调用operator*
,第二个调用operator->
。这与内置类型不同;正如您所指出的,对于内置类型,语法为
base->field
只是
的简写(*base).field
当你谈到
时,会发生类似的事情(*itr).*memberPtr
和
itr->*memberPtr
在第一种情况下,C ++将(*itr).*memberPtr
视为
itr.operator*().*memberPtr
请注意,这意味着.*
运算符直接应用于正在迭代的项,并且迭代器本身不会使.*
运算符重载。另一方面,如果你写
itr->*memberPtr
C ++将其视为对
的调用itr.operator->*(memberPtr)
并报告错误,因为迭代器类型不需要 - 并且很少 - 实现operator ->*
。 (事实上,我在C ++编程过程中看到了operator ->*
的零重载。)
原始类型将base->*memberPtr
视为(*base).*memberPtr
这一事实无关紧要,原因与原始类型将base->field
视为(*base).field
的事实相同;编译器不会自动从operator*
生成operator->
,反之亦然。
有一个单独的问题,为什么迭代器不需要这样做,不幸的是,这是我没有很好的答案。我的猜测是,这是一个非常罕见的案例,没人想到把它放在标准中,但我不确定。
至于为什么这适用于原始数组但不适用于std::vector
,原始数组是使用支持->*
运算符的原始指针。 std::vector
的迭代器不需要是原始指针,如果它们是类类型的对象,上面的推理解释了为什么你不应该期望它们支持operator ->*
。