具有私有基数的函数成员指针

发布于 2024-11-28 04:25:43 字数 605 浏览 2 评论 0原文

以下代码会产生编译时错误:

'base::print':无法访问类“base_der”中声明的私有成员

但是,我已在派生类中将该成员设置为public 。为什么这不起作用?

#include <iostream>

using namespace std;

class base
{
public:
    int i;
    void print(int i)
    {
        printf("base i\n");
    }
};

class base_der : private base
{
public:
    using base::print;
};

int main()
{
    // This works:
    base_der cls;
    cls.print(10);

    // This doesn't:    
    void (base_der::* print)(int);
    print = &base_der::print; // Compile error here
}

The following code yields a compile time error:

'base::print' : cannot access private member declared in class 'base_der'

However, I have made the member public in the derived class. Why doesn't this work?

#include <iostream>

using namespace std;

class base
{
public:
    int i;
    void print(int i)
    {
        printf("base i\n");
    }
};

class base_der : private base
{
public:
    using base::print;
};

int main()
{
    // This works:
    base_der cls;
    cls.print(10);

    // This doesn't:    
    void (base_der::* print)(int);
    print = &base_der::print; // Compile error here
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

素罗衫 2024-12-05 04:25:43

我认为有一些相互作用的问题导致了该错误:

  1. 指向成员类型的指针具有不直观的类型转换特征
  2. using 声明不会影响进入范围的名称的类型
  3. ,而名称 base_der::print< /code> 是可访问的,但类 base 仍然不可访问,并且在尝试转换指向成员的指针时,指向成员类型的指针中的类的实际类型是的考虑。

C++03 7.3.3“using 声明”

using 声明将名称引入到该 using 声明出现的声明区域中。该名称是在其他地方声明的某个实体名称的同义词。

请注意,虽然该名称被引入新的“区域”,但它是一个同义词 - 该名称所指的类型是相同的。因此,我认为在您的示例中,名称 base_der::print 的类型为 void (base::*)(int),而不是类型 void ( base_der::*)(int)

C++03 标准还对指针到成员类型之间的转换进行了规定(4.11“指向成员转换的指​​针”):

“指向 cv T 类型的 B 成员的指针”类型的右值(其中 B 是类类型)可以转换为“指向 cv T 类型的 D 成员的指针”类型的右值,其中 D 是B 的派生类(第 10 条)。如果 B 是 D 的不可访问(第 11 条)、不明确(10.2)或虚拟(10.1)基类,则需要此转换的程序是格式不正确。转换的结果引用与转换发生之前指向成员的指针相同的成员,但它引用基类成员,就好像它是派生类的成员一样。结果引用 D 的 B 实例中的成员。由于结果的类型为“指向 cv T 类型的 D 成员的指针”,因此可以使用 D 对象取消引用它。结果与使用 D 的 B 子对象取消引用指向 B 的成员的指针相同。

另请注意 7.3.3/13“using 声明”(添加了强调):

出于重载决策的目的,通过 using 声明引入派生类的函数将被视为派生类的成员。特别是,隐式 this 参数应被视为指向派生类而不是基类的指针。 这对函数的类型没有影响,并且在所有其他方面,该函数仍然是基类的成员。

现在,生成错误的代码示例:

// This doesn't:    
void (base_der::* print)(int);
print = &base_der::print; // Compile error here

正在尝试转换“指针” D 的成员”到“B 的成员的指针”——这是错误方向的转换。如果您想一下,您就会意识到为什么朝这个方向进行转换并不安全。 “指向 B 成员的指针”类型的变量可能不会与与 D 类有任何关系的对象一起使用 - 但如果您调用类型为“指向 D 成员的指针”的函数(这就是 void (base_der::* print)(int) 是什么),它会正确地期望 this 指针将指向 D< /代码> 对象。

无论如何,虽然我认为问题的根源是这个转换问题,但我认为您会收到有关可访问性的抱怨,因为当编译器尝试处理转换时,它首先检查 base - 即使名称 base_der::print (它是 base::print 的别名)可以访问,因为 using声明,类 base 仍然不是。

免责声明:此分析来自对指向成员类型的指针的细微差别缺乏经验的人。它们是 C++ 的一个领域,非常复杂,除了最简单的场景之外很难使用,并且显然存在很多可移植性问题(请参阅 Doug Clugston 的文章,http://www.codeproject.com/KB/cpp/FastDelegate.aspx,它已经足够老了,现在很多这些问题可能已经得到解决,但我怀疑他们不是)。

当你说 C++ 中的某些东西是更复杂或不太容易理解的领域之一时,这已经说明了很多。

I think there are a few interacting problems contributing to the error:

  1. pointer-to-member types have unintuitive type conversion characteristics
  2. the using declaration doesn't affect the type of the name brought into scope
  3. while the name base_der::print is accessible, the class base still isn't and in an attempt to convert a pointer-to-member, the actual type of the class in the pointer-to-member type is part of the consideration.

C++03 7.3.3 "The using declaration"

A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.

Note that while the name is brought into the new 'region', it's a synonym - the type of what the name refers to is the same. So, I think that in your example, the name base_der::print has a type void (base::*)(int), not type void (base_der::*)(int).

The C++03 standard also says this about conversions between pointer-to-member types (4.11 "Pointer to member conversions"):

An rvalue of type "pointer to member of B of type cv T", where B is a class type, can be converted to an rvalue of type "pointer to member of D of type cv T", where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type "pointer to member of D of type cv T", it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D.

Also note 7.3.3/13 "The using declaration" (emphasis added):

For the purpose of overload resolution, the functions which are introduced by a using-declaration into a derived class will be treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

Now, the code example that generates an error:

// This doesn't:    
void (base_der::* print)(int);
print = &base_der::print; // Compile error here

is trying to convert a "pointer to member of D" to a "pointer to member of B" - which is a conversion in the wrong direction. If you think about it for a moment, you'll realize why a conversion in this direction isn't safe. A variable of type "pointer to member of B" might not be used with an object that has anything to do with class D - but if you call a function with type "pointer to member of D" (which is what void (base_der::* print)(int) is), it'll rightly expect that the this pointer will be pointing to a D object.

Anyway, while I think that the root of the problem is this conversion problem, I think you're getting a complaint about the accessibility because when the compiler is trying to handle the conversion, it's first checking the accessibility of base - and even though the name base_der::print (which is an alias for base::print) is accessible because of the using declaration, class base still isn't.

Disclaimer: this analysis is coming from someone who has little experience in the nuances of pointer-to-member types. They're an area of C++ that is complex, difficult to use except in the simplest scenarios, and apparently have a lot of portability problems (see Doug Clugston's article, http://www.codeproject.com/KB/cpp/FastDelegate.aspx, which is old enough that a lot of these problems may have been addressed by now, but I suspect they aren't).

And when you say that something in C++ is one of the more complex or less-well-understood areas, that's saying a lot.

空心空情空意 2024-12-05 04:25:43

我不能说我知道为什么(我也不能谈论规范),但 clang 的错误消息可能具有启发性:

error: cannot cast private base class 'base' to 'base_der'

因此,更改成员函数的类型至少在 clang 和 gcc 中有效:

void (base::* print)(int);
print = &base_der::print; // works!

I can't say I know why (nor can I speak to the spec), but clang's error message may be instructive:

error: cannot cast private base class 'base' to 'base_der'

So changing the type of the member function works, in clang and gcc at least:

void (base::* print)(int);
print = &base_der::print; // works!
余厌 2024-12-05 04:25:43

这是因为,

class base_der : private base

继承是私有的。因此 base_der 无法访问 base。将其更改为 public 就可以了。

It's because,

class base_der : private base

Inheritance is private. So base is inaccessible to base_der. Change that to public and it will work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文