指向成员奇怪声明的指针

时间:2015-01-24 02:03:20

标签: c++ pointers pointer-to-member

我最近看过以下代码:

template <typename T1, typename T2> 
class Functor
{
    Functor( T1 T2::* t): memPointer(t) {}
    bool operator() ( const T2 &obj1, const T2 &obj )
    {

        return obj1.*memPointer < (obj.*memPointer);

    }

    T1 T2::* memPointer;
};

此处Functor用作通用仿函数,用于对与数据成员对象进行排序,即它的使用方式如

struct ABC
{
    double x;
    double y;
};

int main()
{
    std::vector<ABC> v; 
    // initialize v with ABCs
    Functor<double, ABC> fun(&ABC::x);
    std::sort(std::begin(v), std::end(v), fun); // sort with respect to `ABC::x`
}

我不得不说我不明白Functor是如何运作的。更具体地说,Functor::Functor构造函数的类型是什么? T2::*应该是指向成员的指针,但为什么它与T1合格?我承认我之前没有见过这种语法。

2 个答案:

答案 0 :(得分:3)

这是指向成员语法的bog标准指针。

考虑一个普通的指针:

int * 

在这里,*告诉你它是指针; int告诉您它指向的对象的类型。现在,在

T1 T2::*

T2::*告诉您它是指向班级T2成员的指针。它就像普通指针中的*一样。它指向的成员类型是什么? T1,就像int中的int *一样。

答案 1 :(得分:1)

指向成员的指针定义如何访问该类型对象上的特定成员,与提供对象绝对地址的常规指针进行比较。为了能够访问该成员,您需要两个元素,指向成员的指针和将应用它的对象。

声明T U::*表示这是一个机制,用于访问类型为T的对象内的U类型成员。这两种类型都是必需的,因为T决定了要访问的内容,并且需要U才能知道 如何访问它。特别是,在存在继承的情况下,您可以使用指向成员的指针来基于派生类型的对象,无论基本类型和派生类型是否对齐,编译器都会做正确的事情:

struct base { int member; }                          b;
struct derived1 : base {}                            d1;
struct derived2 : base { virtual void ~derived2(); } d2;
struct anotherbase { int y; };
struct derived3 : anotherbase, base {}               d3;

在上面的代码中,完整对象d1的地址和它的基础子对象是相同的,在d2d3中,基数与派生类型不对齐,由d2 vptr引起d3的情况,anotherbaseint base::*ptm = &base::member; b .*ptm = 5; d1.*ptm = 10; d2.*ptm = 15; d3.*ptm = 20; 而存在{/ 1}}。

b.*ptm

当编译器遇到ptm时,它会将指向成员b的指针应用于对象b.member并生成d1.*ptm。找不到会员所在的位置,无需算术。当遇到d2.*ptm时会发生同样的情况,因为基础和完整对象是对齐的。当遇到d3.*ptmbase::*时,编译器将首先计算基础子对象的地址(指针算术),然后将指向成员的指针应用于该地址。类型Functor表示需要进行的转换(偏移或虚拟继承的动态计算)。 在这个可以访问真实对象的简化示例中,任何名副其实的编译器实际上都会直接注入成员的地址,但如果这是在不同的翻译单元中,并通过引用访问,则上述描述将适用。

除此之外,您创建的std::sort(std::begin(v), std::end(v), [](ABC const& lhs, ABC const & rhs) { return lhs.x < rhs.x; }); 通常会有不良的性能特征,因为您将指针存储到成员并强制使用它。最好将指向成员的指针转换为模板参数,以便编译器具有更好的优化信息。或者,您可以完全避免使用仿函数,只使用具有良好性能的 lambda ,并且可能更容易理解代码的维护者:

x

这也会使您在电话会议中更明显地表示您只使用lhs.x == rhs.x成员,并且可能会提出是否应该重新加入 tie {{1与第二个成员...