【发布时间】:2011-03-04 17:18:52
【问题描述】:
在下面的代码中,有两个使用boost:bind 表达式对std::for_each 的“等效”调用。指示的行编译,指示的失败行失败。我能在标准中找到的最佳解释是“因为我们这么说”。我正在寻找“为什么标准表明这种行为”。我的猜测如下。
我的问题很简单:为什么指定的行编译而等效的下一行无法编译(我不希望因为“标准这么说”,我已经知道 - 我不会接受任何给出的答案这是一个解释;我想解释一下为什么标准这么说)。
注意:虽然我用的是boost,但是boost和这个问题无关,各种格式的错误已经用g++ 4.1.*和VC7.1复现了。
#include <boost/bind.hpp>
#include <iostream>
#include <map>
#include <algorithm>
class Base
{
protected:
void foo(int i)
{ std::cout << "Base: " << i << std::endl; }
};
struct Derived : public Base
{
Derived()
{
data[0] = 5;
data[1] = 6;
data[2] = 7;
}
void test()
{
// Compiles
std::for_each(data.begin(), data.end(),
boost::bind(&Derived::foo, this,
boost::bind(&std::map<int, int>::value_type::second, _1)));
// Fails to compile - why?
std::for_each(data.begin(), data.end(),
boost::bind(&Base::foo, this,
boost::bind(&std::map<int, int>::value_type::second, _1)));
}
std::map<int, int> data;
};
int main(int, const char**)
{
Derived().test();
return 0;
}
指示的行因以下错误而失败: main.C:在成员函数'void Derived::test()'中: main.C:9:错误:'void Base::foo(int)' 受保护 main.C:31:错误:在此上下文中
如前所述,上面假定等效的语句可以干净地编译(如果有问题的语句被注释掉,则运行时会以在不同行上打印“5”、“6”、“7”的预期结果运行)。
在寻找解释时,我在标准中遇到了 11.5.1(具体来说,我正在查看 2006-11-06 草案):
额外的访问检查 前面第 11 条中描述的那些 应用于非静态数据时 成员或非静态成员函数是 其命名类的受保护成员 (11.2)105) 如前所述, 访问受保护的成员是 因为引用发生而授予 在某个 C 类的朋友或成员中。 如果访问是形成一个指向的指针 成员 (5.3.1), 嵌套名称说明符应命名为 C 或 从 C 派生的类。所有其他 访问涉及(可能是隐式的) 对象表达式(5.2.5)。在这个 案例,对象的类 表达式应为 C 或类 源自 C。
读完后,很明显为什么第二个语句失败而第一个语句成功了,但是问题来了:这是什么原理?
我最初的想法是编译器正在扩展 boost::bind 模板,发现 Base::foo 受到保护并将其踢出,因为 boost::bind<...> 不是朋友。但是,我对这个解释想得越多,它就越没有意义,因为如果我没记错的话,一旦你把指针指向一个成员(假设你最初是在成员的访问控制范围内),所有的访问控制信息都是丢失(即我可以定义一个函数,该函数返回一个指向成员的任意指针,该成员根据某些输入交替返回一个公共、受保护或私有成员,而返回者不会更聪明)。
我想了很多,我能想出的唯一合理的解释为什么它应该有所作为是在多重继承的情况下。具体来说,根据类布局,从 Base 计算的成员指针与从 Derived 计算的成员指针不同。
【问题讨论】:
-
我会立即注意到:这可能需要一个更好的主题。如果有人能给我一个更好的,我会改变它。提前谢谢。
-
因为标准是这么说的。
-
看起来
Base::foo应该有virtual。加进去有效果吗? -
@zildjohn01 我没有覆盖 Base::foo,所以没有。它纯粹是试图访问基础成员。另请注意,不涉及模板,因此也不是依赖名称问题。
-
看起来有一个更简单的案例在相同的错误上失败。不需要 boost 绑定,您需要做的就是在
test()中写入&Base::Foo;并且它会失败。我也很困惑为什么会这样。
标签: c++