Background
I have a class containing different members (custom run time constructed structs). And I have a compile time tuple containing pairs of pointer-to-member elements and strings. Compile time I need to check if every pointer-to-member and name is used only once in the list, and the custom structs check if they have an entry in the tuple (they know their own pointer-to-member). Having a tuple for this purpose increases the compile time dramatically, it would be great to identify the members in compile time with a void* array and not with a heterogeneous data struct.
Attempt to solve problem
As I read in this thread, dereferencing a nullptr is not always undefined behavior.
I read CWG-issue #315 also, that states:
We agreed the example should be allowed. p->f() is rewritten as (*p).f() according to 5.2.5 [expr.ref]. *p is not an error when p is null unless the lvalue is converted to an rvalue (4.1 [conv.lval]), which it isn't here.
I wanted to leverage this to get a normal pointer from a pointer-to-member (I don't want to dereference them, I just want to compare pointers-to-members from the same class but with different types).
So I created the following code:
#include <iostream>
class Test
{
int a;
public:
static constexpr inline int Test::*memPtr = &Test::a;
static constexpr inline int* intPtr = &(static_cast<Test*>(nullptr)->*Test::memPtr);
};
int main () {
std::cout << Test::intPtr << std::endl;
}
In my opinion the &(static_cast<Test*>(nullptr)->*Test::memPtr); expression uses the same approach as the code that was discussed in CWG-issue #315.
The code above compiles with MSVC but not with clang or gcc.
I checked if similar code that was mentioned in #315 compiles or not:
struct Test {
static constexpr int testFun () { return 10; }
};
int main ()
{
static constexpr int res{static_cast<Test*>(nullptr)->testFun()};
static_assert(res == 10, "error");
}
And yes, it does. test code
Should the construct I used in the first example be available in constexpr expressions (as undefined behavior is not allowed there)?
Fun fact: If I modify my original code and add a virtual destructor to the class then both MSVC and clang are happy with it, and gcc crashes. I mean literally, it segfaults.
Fun fact 2: If I remove the virtual destructor and make the class templated gcc and MSVC compiles it, but now clang complains.
From the standard on ->*'s behavior:
The expression E1->*E2 is converted into the equivalent form (*(E1)).*E2.
And for .*:
Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined.
The dynamic type of E1 (which dereferences a nullptr) does not exist, because it's a reference to no object. Therefore, the behavior of this expression is undefined.
Related
C++20 allows heap allocation in constexpr functions as long as the memory does not leak. However GCC and Clang disagree on whether comparing the addresses of two dynamically allocated objects is a constant expression or not.
The following snippet can be compiled with Clang but not gcc.
constexpr bool foo() {
int* a = new int(4);
int* b = new int(4);
bool result = a == b;
delete a;
delete b;
return result;
}
constexpr bool x = foo(); // GCC: error: '(((int*)(& heap deleted)) == ((int*)(& heap deleted)))' is not a constant expression
The following works fine on both compilers
constexpr bool foo2() {
int a = 4;
int b = 5;
bool result = &a == &b;
return result;
}
constexpr bool x = foo2();
I'd assume that in order to delete the dynamic objects correctly the compiler must know whether the pointers point to the same objects or not, so I'd assume this is a GCC bug (or not yet fully implemented). Can anyone confirm this assumption? Or am I wrong?
Live example here.
Edit: Strangely, when I open the live example through the link provided, it suddenly compiles on gcc, too. But if I copy-paste it to a new compiler explorer instance, it fails again. Or if I reload it multiple times it fails every second time and compiles every other second time...
This is a gcc bug (#85428).
There's nothing in [expr.const]/5 that would cause evaluation of a == b to fail to be a constant expression. The only one there in which there is any question would be the one about undefined behavior. So we could go look at [expr.eq] to see what that says about pointer comparison:
If at least one of the operands is a pointer, pointer conversions, function pointer conversions, and qualification conversions are performed on both operands to bring them to their composite pointer type.
Comparing pointers is defined as follows:
If one pointer represents the address of a complete object, and another pointer represents the address one past the last element of a different complete object, the result of the comparison is unspecified.
Otherwise, if the pointers are both null, both point to the same function, or both represent the same address, they compare equal.
Otherwise, the pointers compare unequal.
Both pointers represent the address of different complete objects, with neither being null, so so we fall into the third bullet point and the pointers just compare unequal. No undefined or unspecified behavior here.
a == b should just yield false.
C++20 allows heap allocation in constexpr functions as long as the memory does not leak. However GCC and Clang disagree on whether comparing the addresses of two dynamically allocated objects is a constant expression or not.
The following snippet can be compiled with Clang but not gcc.
constexpr bool foo() {
int* a = new int(4);
int* b = new int(4);
bool result = a == b;
delete a;
delete b;
return result;
}
constexpr bool x = foo(); // GCC: error: '(((int*)(& heap deleted)) == ((int*)(& heap deleted)))' is not a constant expression
The following works fine on both compilers
constexpr bool foo2() {
int a = 4;
int b = 5;
bool result = &a == &b;
return result;
}
constexpr bool x = foo2();
I'd assume that in order to delete the dynamic objects correctly the compiler must know whether the pointers point to the same objects or not, so I'd assume this is a GCC bug (or not yet fully implemented). Can anyone confirm this assumption? Or am I wrong?
Live example here.
Edit: Strangely, when I open the live example through the link provided, it suddenly compiles on gcc, too. But if I copy-paste it to a new compiler explorer instance, it fails again. Or if I reload it multiple times it fails every second time and compiles every other second time...
This is a gcc bug (#85428).
There's nothing in [expr.const]/5 that would cause evaluation of a == b to fail to be a constant expression. The only one there in which there is any question would be the one about undefined behavior. So we could go look at [expr.eq] to see what that says about pointer comparison:
If at least one of the operands is a pointer, pointer conversions, function pointer conversions, and qualification conversions are performed on both operands to bring them to their composite pointer type.
Comparing pointers is defined as follows:
If one pointer represents the address of a complete object, and another pointer represents the address one past the last element of a different complete object, the result of the comparison is unspecified.
Otherwise, if the pointers are both null, both point to the same function, or both represent the same address, they compare equal.
Otherwise, the pointers compare unequal.
Both pointers represent the address of different complete objects, with neither being null, so so we fall into the third bullet point and the pointers just compare unequal. No undefined or unspecified behavior here.
a == b should just yield false.
This is a slightly esoteric question, but I was curious whether the following class extension pattern is legal (as in, does not constitute UB) in modern C++ (for all intends and purposes I am fine with restricting the discussion to C++17 and later).
template<typename T>
struct AddOne {
T add_one() const {
T const& tref = *reinterpret_cast<T const*>(this);
return tref + 1;
}
};
template<template<typename> typename E, typename T>
E<T> const& as(T const& obj) {
return reinterpret_cast<E<T> const&>(obj);
}
auto test(float x) {
return as<AddOne>(x).add_one();
}
auto test1(int x) {
return as<AddOne>(x).add_one();
}
// a main() to make this an MVCE
// will return with the exit code 16
int main(int argc, const char * argv[]) {
return test1(15);
}
The above code is a complete example, it compiles, runs and produces the expected result with at least clang in C++17 mode. Check the disassembly code on compiler explorer: https://godbolt.org/z/S3ZX2Y
My interpretation is as follows: the standard states that reinterpret_cast can convert between pointers/references of any types, but that accessing there resulting references might be UB (as per to aliasing rules). At the same time, converting the resulting value back to the original type is guaranteed to yield the original value.
Based on this, merely reinterepting a reference to float as a reference to AddOne<float> does not invoke UB. Since we never attempt to access any memory behind that reference as instance of AddOne<float>, there is no UB here either. We only use the type information of that reference to select the correct implementation of add_one() member function. The function itself coverts the reference back to the original type, so again, no UB. Essentially, this pattern is semantically equivalent to this:
template<typename T>
struct AddOne {
static T add_one(T const& x) {
return x + 1;
}
};
auto test(float x) {
return AddOne<Int>::add_one(x);
}
Am I correct or is there something I miss here?
Consider this as an academic exercise in exploring the C++ standard.
Edit: this is not a duplicate of When to use reinterpret_cast? since that question does not discuss casting this pointer or using reinterpret_cast to dispatch on the reinterpreted type.
No, that's definitely not legal. For a number of reasons.
The first reason is, you've got *this dereferencing an AddOne<int>* which doesn't actually point to an AddOne<int>. It doesn't matter that the operation doesn't really require a dereference "behind the scenes"; *foo is only legal if foo points to an object of compatible type.
The second reason is similar: You're calling a member function on an AddOne<int> which isn't. It likewise doesn't matter that you don't access any of AddOne's (nonexistent) members: the function call itself is an access of the object value, running afoul of the strict aliasing rule.
The full answer was provided by user #n.m in the comments, so I will copy it here for the sake of completeness.
A paragraph from [basic.life] c++ standard sections states:
Before the lifetime of an object has started [...] The program has undefined
behavior if [...]
the pointer is used to access a non-static data member or call a non-static member function of the object
As to seems, this prohibits dispatch via the reinterpreted reference since that reference does not refer to a live object.
It is, as far as I have known, been a good rule that a pointer like argument type to a function should be a pointer if the argument can sensible be null and it should be a reference if the argument should never be null.
Based on that "rule", I have naiively expected that doing something like
someMethodTakingAnIntReference(*aNullPointer) would fail when trying to make the call, but to my surprise the following code is running just fine which kinda makes "the rule" less usable. A developer can still read meaning from the argument type being reference, but the compiler doesn't help and the location of the runtime error does not either.
Am I misunderstanding the point of this rule, or is this undefined behavior, or...?
int test(int& something1, int& something2)
{
return something2;
}
int main()
{
int* i1 = nullptr;
int* i2 = new int{ 7 };
//this compiles and runs fine returning 7.
//I expected the *i1 to cause an error here where test is called
return test(*i1, *i2);
}
While the above works, obviously the following does not, but the same would be true if the references were just pointers; meaning that the rule and the compiler is not really helping.
int test(int& something1, int& something2)
{
return something1+something2;
}
int main()
{
int* i1 = nullptr;
int* i2 = new int{ 7 };
//this compiles and runs returning 7.
//I expected the *i1 to cause an error here where test is called
return test(*i1, *i2);
}
Writing test(*i1, *i2) causes undefined behaviour; specifically the part *i1. This is covered in the C++ Standard by [expr.unary.op]/1:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
This defines the behaviour of *X only for the case where X points to an object or function. Since i1 does not point to an object or function, the standard does not define the behaviour of *i1, therefore it is undefined behaviour. (This is sometimes known as "undefined by omission", and this same practice handles many other uses of lvalues that don't designate objects).
As described in the linked page, undefined behaviour does not necessitate any sort of diagnostic message. The runtime behaviour could literally be anything. The compiler could, but is not required to, generate a compilation warning or error. In general, it's up to the programmer to comply with the rules of the language. The compiler helps out to some extent but it cannot cover all cases.
You're better off thinking of references as little more than a handy notation for pointers.
They are still pointers, and the runtime error occurs when you use (dereference) a null pointer, not when you pass it to a function.
(An added advantage of references is that they can not be changed to reference something else, once initialized.)
I have a code.
class A
{
public:
int foo(int i)
{
return i;
}
};
int foo(int i)
{
return i;
}
int (A::*ptrFoo)(int) = NULL;
int (*_foo)(int) = NULL;
int main()
{
ptrFoo = &A::foo;
_foo = foo;
(*_foo)++++++++++++++(10); //This dont compile...
A a;
(a.*ptrFoo)+++++++++++++++++(10); //This compiles ????
}
please tell me what it is ?? a undefined behavior or what ??? I compiled it in VS2008.Strangely the last line of code compiles successfully.
Neither expression should compile: in C++, you cannot perform arithmetic on a pointer to a function or member function, or on a function type or member function. The two expressions in your program attempt to perform arithmetic on a function and on a member function, respectively.
If your compiler accepts the second expression, it is due to a bug in the compiler.
First note that pointer to functions are different with pointer to member functions.
Your first example is a pointer to an ordinary function. It contains the real memory address of the function. When you dereference it ((*_foo)) you get the function itself, and arithmetic operations including ++ on a function (function pointer) are meaningless.
The second one is another story, pointers to member functions of classes do not carry the address of the function in memory. Actually how compiler manages member functions is implementation-specific. A pointer to a member function may contain some address or maybe some compiler-specific information. Arithmetic on this type is also meaningless.
Therefore we don't know what the value of (a.*ptrFoo) ever is, but in your case MSVC2008 managed to compiler it, either because of a bug or by design.
By the way, GCC does not compile any of the two statements and threw errors on both.
The above is true whether you put even number of +'s or odd numbers; we are doing arithmetic anyway. (If there are an odd number of +'s then there is no function call, as in your second example you are incrementing the function 8 times then the last remaining + adds 10 to the result. Again, this doesn't matter: we are trying to change a function/member function pointer.)