...
(A pointer-to-member expression of the form E1->*E2
is converted to its equivalent form, (*(E1)).*E2
, so use of pointer-to-member expressions of either form behave equivalently in terms of undefined behavior.)
Further, the C++ Standard, [expr.mptr.oper], paragraph 6 states, in part:
If the second operand is the null pointer to member value, the behavior is undefined.
Do not use a pointer-to-member expression where the dynamic type of the first operand does not contain the member to which the second operand refers, including the use of a null pointer-to-member value as the second operand.
Noncompliant Code Example
...
Code Block | ||||
---|---|---|---|---|
| ||||
struct B {
virtual ~B() = default;
};
struct D : B {
virtual ~D() = default;
virtual void g() { /* ... */ }
};
void f() {
B *b = new B;
// ...
void (B::*gptr)() = static_cast<void(B::*)()>(&D::g);
(b->*gptr)();
delete b;
}
|
Compliant Solution
In this compliant solution, the upcast is removed, rendering the initial code ill-formed. This emphasizes the underlying problem: that B::g() does not exist. This compliant solution assumed the programmer intent was to use the correct dynamic type for the underlying object:
Code Block | ||||
---|---|---|---|---|
| ||||
struct B {
virtual ~B() = default;
};
struct D : B {
virtual ~D() = default;
virtual void g() { /* ... */ }
};
void f() {
B *b = new D; // Corrected dynamic object type
// ...
void (D::*gptr)() = &D::g; // Removed static_cast
(static_cast<D *>(b)->*gptr)();
delete b;
}
|
Noncompliant Code Example
In this noncompliant code example, a null pointer-to-member value is passed as the second operand to a pointer-to-member expression, resulting in undefined behavior:
Code Block | ||||
---|---|---|---|---|
| ||||
struct B {
virtual ~B() = default;
};
struct D : B {
virtual ~D() = default;
virtual void g() { /* ... */ }
};
static void (D::*gptr)();
void call_memptr(D *ptr) {
(ptr->*gptr)();
}
void f() {
D *d = new D;
call_memptr(d);
delete d;
} |
Compliant Solution
In this compliant solution, gptr
is properly initialized to a valid pointer-to-member value, instead of the default value of nullptr
:
Code Block | ||||
---|---|---|---|---|
| ||||
struct B { virtual ~B() = default; }; struct D : B { virtual ~D() = default; virtual void g() { /* ... */ } }; static void (D::*gptr)() = &D::g; void call_memptr(D *ptr) { (ptr->*gptr)(); } void f() { D *d = new D; call_memptr(d); delete d; } |
Risk Assessment
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
OOP39-CPP | High | Probable | High | P6 | L2 |
...
[ISO/IEC 14882-2014] | 5.5, "Pointer-to-Member Operators" |
OOP37-CPP. Write constructor member initializers in the canonical order 013. Object Oriented Programming (OOP) 014. Concurrency (CON)