Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

What is the meaning of this combination of the arrow operator -> and scope resolution operator ::?

On cppreference.com – “Qualified name lookup”, I found this strange code example:

struct C { typedef int I; };

typedef int I1, I2;

extern int *p, *q;

// Slight modification to prevent linker error
//struct A { ~A(); };
struct A { ~A() {}; };

typedef A AB;

int main()
{
    p->C::I::~I(); // The name I after ~ is looked up in the same scope as I before ::
                   // (that is, within the scope of C, so it finds C::I)

    q->I1::~I2();  // The name I2 is looked up in the same scope as I1
                   // (that is, from the current scope, so it finds ::I2)

    AB x;
    x.AB::~AB();   // The name AB after ~ is looked up in the same scope as AB before ::
                   // (that is, from the current scope, so it finds ::AB)
}

To my surprise, this compiles without any errors. But what is even going in the lines

p->C::I::~I();
q->I1::~I2();

? Not only does this look like it’s accessing members of int variables and somehow referring to an int destructor, but p and q are also extern variables without any definition.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

How come that this syntax is allowed, and what does it actually do?

>Solution :

and somehow referring to an int destructor,

These are pseudo-destructor calls. The direct notation not using the qualified lookup of type aliases would be

p->~int();
q->~int();

The notation x.~T() or p->~T() is always a pseudo-destructor call if T is a scalar type, rather than a class type. And in that case x must have type T and p type int*.

A pseudo-destructor call ends the lifetime of the object x/*p, just like a normal destructor call, but doesn’t do anything else. In particular it doesn’t call any destructor (which scalar types do not have). It exists only so that one can write generic code that works for both class and non-class types without having to special case destructor calls.

Not only does this look like it’s accessing members of int variables

This is explained in the comments and the linked cppreference article itself. The notation p-> followed by a qualified name does not imply that a member of the type of *p is looked up. Instead lookup may also consider the current scope, given the rules provided in the article. However, if *p is not a class type, then a pseudo-destructor call is the only result of such a lookup that wouldn’t cause the expression to end up ill-formed.

but p and q are also extern variables without any definition.

The pseudo-destructor calls odr-use p and q, so a definition for them must exist in the program. However, it may be in a different translation unit. And even if it doesn’t exist, such an odr violation makes the program IFNDR (ill-formed, no diagnostic required), so the compiler doesn’t have to complain about it. The lines don’t actually cause any machine code to be emitted after all, so there is no reason to bother the linker to look for a definition of p and q.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading