不能使用指向来自私有基的公共成员函数的指针

互联网 行业动态 更新时间:2024-06-13 00:19:06

Tur*_*ght 14

tl;博士:

声明成员的类是成员函数指针将绑​​定到的类。 ->*on aDerived不能与Base::成员函数指针一起使用,除非您可以访问private Basein (例如在 的成员函数中或在声明为 的朋友的函数中)。DerivedDerivedDerived c 风格的转换允许您转换Derived*Base*这些类型的成员函数指针,即使Base不可访问(这对于任何 c++ 风格的转换都是非法的),例如:
Base* b = (Base*)&d;
在你的例子中是合法的。

1. 为什么你得到一个Base::成员函数指针

9.9using声明(强调我的)

12 [注5:为了在重载决议期间形成一组候选者,派生类中使用声明命名的函数被视为派生类的直接成员。特别是,隐式对象参数被视为对派生类的引用,而不是对基类 ( [over.match.funcs] ) 的引用。这对函数的类型没有影响,并且在所有其他方面,该函数仍然是基类的一部分。——尾注]

所以using-declaration 不会创建foofor的版本Derived,但编译器需要假装它是Derived::重载决议的成员。

所以在这种情况下,相关位是这对函数的类型没有影响——也就是说,Base::如果你获取foo.

注意:唯一的例外是构造函数

2. 为什么不能使用->*成员Base::指针

7.6.4 指向成员运算符(强调我的)

3二元运算符->*将它的第二个操作数绑定到它的第一个操作数,类型应该指向T成员可访问的基类。表达式被转换为等价形式。E1->*E2(*(E1)).*E2

这里的问题是,在你使用foo指针的时候Base不可访问的,所以调用不起作用。


3.如何使它工作

成员函数实际上需要可转换为任何派生类型,只要满足以下几个条件:

7.3.13 指针到成员的转换(强调我的)

2 类型为“指向类型为cv T的B成员的指针” 类型的纯右值,其中B是类类型,可以转换为类型为“指向类型为cv T的D成员的指针” 类型的纯右值,其中D是完整类从B派生([class.derived] ) 。如果B是 D 的不可访问( [class.aess] )、不明确( [class.member.lookup] ) 或虚拟( [class.mi] ) 基类,或D的虚拟基类的基类,需要这种转换的程序是不正确的。
[...]

鉴于这Base不像您的示例那样模棱两可或虚拟,我们需要关注的唯一问题是可访问性部分。

还是我们?

实际上,我们可以使用标准中的一个小漏洞:

7.6.3 显式类型转换(强制转换符号)(强调我的)

4执行的转换

(4.1)一个const_­cast( [expr.const.cast] ), (4.2)一个static_­cast([expr.static.cast]), (4.3) astatic_­cast后跟 a const_­cast, (4.4) a reinterpret_­cast( [expr.reinterpret.cast] ),或 (4.5) areinterpret_­cast后跟 a const_­cast,

可以使用显式类型转换的强制转换表示法来执行。相同的语义限制和行为适用,但static_­cast在以下情况下执行转换是有效的,即使基类不可访问

(4.6)指向派生类类型对象的指针或派生类类型的左值或右值可以分别显式转换为指向明确基类类型的指针或引用; (4.7)指向派生类类型成员的指针可以显式转换为指向明确非虚基类类型成员的指针; (4.8)指向明确非虚拟基类类型对象的指针、明确非虚拟基类类型的glvalue或指向明确非虚拟基类类型成员的指针可以显式转换为指针、引用或指向派生类类型成员的指针

[...]

因此,尽管任何 C++ 转换方法(如static_cast/reinterpret_cast等)都不允许从Base::*to转换,但允许Derived::*c 样式转换执行它,即使在基类不可访问时也是如此

例如:

int main() {
  Derived d;
  auto fn = &Derived::foo;

  // cast to base (only legal with c-style cast)
  // Base* b = static_cast<Base*>(&d); // not legal
  // Base* b = reinterpret_cast<Base*>(&d); // not legal
  Base* b = (Base*)&d; // legal
  (b->*fn)(12);

  // cast member function pointer to derived
  // (also only legal with c-style cast)
  using MemFn = int (Derived::*)(int) const;
  // auto fnD = static_cast<MemFn>(fn); // not legal
  // auto fnD = reinterpret_cast<MemFn>(fn); // not legal
  auto fnD = (MemFn)fn; // legal
  (d.*fnD)(12);

  // or as a one liner (provided by @KamilCuk in the ments):
  // slightly hard to read, but still legal c++:
  (d.*((int(decltype(d)::*)(int))&decltype(d)::foo))(12); // legal
}

螺栓示例

是有效的 C++。

因此,只需将DerivedtoBase或成员函数指针强制转换为绑定到Derived.

标准中甚至有一个例子可以做到这一点:11.8.3 基类和基类成员的可访问性(3)


4、为什么不&Derived::foo返回Derived::*memfn指针?

因为标准是这样说的。我不知道他们为什么会这样决定,但我可以推测可能的原因是什么:

您可以检查哪个派生最多的类实现了给定的成员函数。&Derived::foo如果返回Derived::*指针,这将中断。(例如,这可以与 CRTP 一起使用,以检查给定Derived类是否为 的给定成员提供了新定义Base)例如:

class Base {
public:
    int foo(int x) const { return 2*x; }
};

class Derived : private Base {
public:
    using Base::foo;
};

template<class T>
struct implementing_class_helper;

template<class T, class R>
struct implementing_class_helper<R T::*> {
    typedef T type;
};

template<class T>
struct implementing_class : implementing_class_helper<typename std::remove_cv<T>::type> {

};

template<class T>
using implementing_class_t = implementing_class<T>;

int main() {
  static_assert(std::is_same_v<
    typename implementing_class<decltype(&Derived::foo)>::type,
    Base
  >, "Shenanigans!");
}

如果要创建存根函数,例如:

class Base {
public:
  int foo(int x) const { return 2*x; }
};

class Derived : Base {
public:
  // pretending using Base::foo; would result in this:
  int foo(int x) { return Bar::foo(x); }
};

编译器现在会遇到问题,因为Base::fooDerived::foo是不同的函数,但仍然需要比较等于Base::foo,因为这是实际的实现。
因此,编译器需要知道所有using Base::foo;编译单元中包含的所有类,并确保无论何时将它们::foo与结果进行比较Base::foo,结果都是true. 对于一个奇怪的边缘情况,这听起来像是很多实现工作。

`(d.*((int(decltype(d)::*)(int))&amp;decltype(d)::foo))(12);`这很有趣 (2认同) @KamilCuk 绝对应该因为默默无闻而获奖 :D 但它仍然是合法的 c++ :) (2认同)

更多推荐

指针,函数,成员

本文发布于:2023-04-20 20:41:48,感谢您对本站的认可!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:指针   函数   成员

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!