指向成员的指针的语法糖适用于数组但不适用于 std::vector
Syntax sugar for pointer-to-member works for array but not std::vector
我写了一些示例代码(如下)来理解 C++ 的 成员指针 特性。但是,我遇到了一个奇怪的问题,其中语法
(*it).*attribute
被编译器接受,但是语法
it->*attribute
不被接受,错误
left hand operand to ->*
must be a pointer to class compatible with the right hand operand, but is std::__1::__wrap_iter<Bowl *>
但是,如果我取消注释 Bowl bowls[3] =
并注释掉 std::vector<Bowl> bowls =
,即从使用 std::vector
切换到使用原始数组,那么两种语法都可以正常工作。
从C++ reference,我发现
The expression E1->*E2
is exactly equivalent to (*E1).*E2
for built-in types
所以错误似乎与数组是内置类型但 std::vector
不是。在该部分的后面,我发现
In overload resolution against user-defined operators, for every combination of types D, B, R, where class type B is either the same class as D or an unambiguous and accessible base class of D, and R is either an object or function type, the following function signature participates in overload resolution:
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;
}
为了更好地理解这一点,我认为讨论与 *
和 ->
运算符的相似之处会有所帮助。
如果你有一个 class 类型的迭代器并且你写
(*itr).field
C++ 将其解释为
itr.operator*().field
同样,如果你写
itr->field
C++ 将其解释为
itr.operator->().field
请注意,它们正在调用不同的重载运算符。第一个调用 operator*
,第二个调用 operator->
。这与内置类型不同;如您所述,对于内置类型,语法
base->field
只是shorthand
(*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
的迭代器不需要是原始指针,如果它们是 class 类型的对象,上述推理解释了为什么你不应该期望它们支持 operator ->*
。
我写了一些示例代码(如下)来理解 C++ 的 成员指针 特性。但是,我遇到了一个奇怪的问题,其中语法
(*it).*attribute
被编译器接受,但是语法
it->*attribute
不被接受,错误
left hand operand to
->*
must be a pointer to class compatible with the right hand operand, but isstd::__1::__wrap_iter<Bowl *>
但是,如果我取消注释 Bowl bowls[3] =
并注释掉 std::vector<Bowl> bowls =
,即从使用 std::vector
切换到使用原始数组,那么两种语法都可以正常工作。
从C++ reference,我发现
The expression
E1->*E2
is exactly equivalent to(*E1).*E2
for built-in types
所以错误似乎与数组是内置类型但 std::vector
不是。在该部分的后面,我发现
In overload resolution against user-defined operators, for every combination of types D, B, R, where class type B is either the same class as D or an unambiguous and accessible base class of D, and R is either an object or function type, the following function signature participates in overload resolution:
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;
}
为了更好地理解这一点,我认为讨论与 *
和 ->
运算符的相似之处会有所帮助。
如果你有一个 class 类型的迭代器并且你写
(*itr).field
C++ 将其解释为
itr.operator*().field
同样,如果你写
itr->field
C++ 将其解释为
itr.operator->().field
请注意,它们正在调用不同的重载运算符。第一个调用 operator*
,第二个调用 operator->
。这与内置类型不同;如您所述,对于内置类型,语法
base->field
只是shorthand
(*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
的迭代器不需要是原始指针,如果它们是 class 类型的对象,上述推理解释了为什么你不应该期望它们支持 operator ->*
。