Aliasing T* with char* is allowed. Is it also allowed the other way around?
Asked Answered
O

3

28

Note: This question has been renamed and reduced to make it more focused and readable. Most of the comments refer to the old text.


According to the standard, objects of different type may not share the same memory location. So this would not be legal:

std::array<short, 4> shorts;
int* i = reinterpret_cast<int*>(shorts.data()); // Not OK

The standard, however, allows an exception to this rule: any object may be accessed through a pointer to char or unsigned char:

int i = 0;
char * c = reinterpret_cast<char*>(&i); // OK

However, it is not clear to me whether this is also allowed the other way around. For example:

char * c = read_socket(...);
unsigned * u = reinterpret_cast<unsigned*>(c); // huh?
Outshoot answered 27/9, 2012 at 0:45 Comment(24)
I don't believe the second one is valid. Dereferencing i will break strict-aliasing.Limbert
This is where memcpy comes in...Plerre
@Limbert doesn't new int do something like static_cast<int*>(malloc(sizeof(int)) under the hood?Outshoot
What does new int have to do with it? EDIT: Oh, I believe malloc() is one of the exceptions. I'm not exactly sure what the standard says. But going back to you second case, suppose c is misaligned and the processor doesn't support misaligned access.Limbert
Ah, so c could be misaligned. I had not considered that yet.Outshoot
You're allowed to cast malloc() to any of the built-in types since it is guaranteed to be aligned to all the built-in types. But if you put a char[] on the stack, it's not guaranteed to be aligned to an int. I don't know how the standard words it though.Limbert
Only the first one is valid: you can interpret everything as an array of chars. However, you can not interpret an array of chars as anything else.Suck
@KerrekSB So I can not cast the result of malloc to a certain type? Or is it basically a question of alignment as Mysticial seems to suggest.Outshoot
@Outshoot You most certainly can. But I'm not sure how the standard words it to allow this exception.Limbert
The result of malloc is guaranteed to be maximally aligned, so you can use it for any built-in type. Overaligned types need your own memory handling. (But malloc has nothing to do with type punning or aliasing.)Suck
I believe new char[] is also guaranteed to be maximally aligned. Otherwise it's hard to write template containers. EDIT: #507018Limbert
Re the "what about this", same issue, namely alignment. gotta get that alignment right.Indevout
@Cheersandhth.-Alf does the "what about this"-code break aliasing rules (on top of breaking alignment rules)?Outshoot
@StackerCrooked: ìt seems OK (when disregarding alignment). any POD can be accessed as sequence of char, and vice versa. but i would use a placement new instead of a reinterpret_cast...Indevout
@Cheersandhth.-Alf Are you sure about the vice versa? I keep hearing that you can't alias a char array with another type.Limbert
wait, i need to copy standard docs from USB driveIndevout
3.9.2 "For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value"Indevout
alignas(int) char c[sizeof(int)]; should be enough to obtain proper storage for an int. std::aligned_storage<sizeof(int), alignof(int)>::type c; is an alternative.Winstead
also 3.10.10 last dash "a char or unsigned char type."Indevout
@Cheersandhth.-Alf I suppose placement new is preferred because it also calls the constructor (if there is one). However, I assume it doesn't help with alignment?Outshoot
The rules of the standard don't apply to the code of the implementation; new can do anything it wants in order to achieve its spec.Skillless
@StackedCrooked: yes, placement new is more clean and more robust (calling constructor). and AFAIK it doesn't help with alignment.Indevout
@Plerre Good point. Since memcpy has 'extremely defined' behaviour and, I've read, may be optimised into the same code as the questioned case here - I often just use that, rather than breaking my brain trying to extract any real answer from the terrible combination of arcane Standardese and useless personal arguments in threads like this.Laky
@underscore_d: The C 99 Standard explicitly specifies that memcpy can be used to read storage written as data of an unknown type into storage with a declared type, but can generally not be used to write data which was written with one type in such a way as to be readable using another. The C++ Standard doesn't explicitly specify how memcpy works, except to say that it works as it does in C.Buonaparte
W
23

Some of your code is questionable due to the pointer conversions involved. Keep in mind that in those instances reinterpret_cast<T*>(e) has the semantics of static_cast<T*>(static_cast<void*>(e)) because the types that are involved are standard-layout. (I would in fact recommend that you always use static_cast via cv void* when dealing with storage.)

A close reading of the Standard suggests that during a pointer conversion to or from T* it is assumed that there really is an actual object T* involved -- which is hard to fulfill in some of your snippet, even when 'cheating' thanks to the triviality of types involved (more on this later). That would be besides the point however because...

Aliasing is not about pointer conversions. This is the C++11 text that outlines the rules that are commonly referred to as 'strict aliasing' rules, from 3.10 Lvalues and rvalues [basic.lval]:

10 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

(This is paragraph 15 of the same clause and subclause in C++03, with some minor changes in the text with e.g. 'lvalue' being used instead of 'glvalue' since the latter is a C++11 notion.)

In the light of those rules, let's assume that an implementation provides us with magic_cast<T*>(p) which 'somehow' converts a pointer to another pointer type. Normally this would be reinterpret_cast, which yields unspecified results in some cases, but as I've explained before this is not so for pointers to standard-layout types. Then it's plainly true that all of your snippets are correct (substituting reinterpret_cast with magic_cast), because no glvalues are involved whatsoever with the results of magic_cast.

Here is a snippet that appears to incorrectly use magic_cast, but which I will argue is correct:

// assume constexpr max
constexpr auto alignment = max(alignof(int), alignof(short));
alignas(alignment) char c[sizeof(int)];
// I'm assuming here that the OP really meant to use &c and not c
// this is, however, inconsequential
auto p = magic_cast<int*>(&c);
*p = 42;
*magic_cast<short*>(p) = 42;

To justify my reasoning, assume this superficially different snippet:

// alignment same as before
alignas(alignment) char c[sizeof(int)];

auto p = magic_cast<int*>(&c);
// end lifetime of c
c.~decltype(c)();
// reuse storage to construct new int object
new (&c) int;

*p = 42;

auto q = magic_cast<short*>(p);
// end lifetime of int object
p->~decltype(0)();
// reuse storage again
new (p) short;

*q = 42;

This snippet is carefully constructed. In particular, in new (&c) int; I'm allowed to use &c even though c was destroyed due to the rules laid out in paragraph 5 of 3.8 Object lifetime [basic.life]. Paragraph 6 of same gives very similar rules to references to storage, and paragraph 7 explains what happens to variables, pointers and references that used to refer to an object once its storage is reused -- I will refer collectively to those as 3.8/5-7.

In this instance &c is (implicitly) converted to void*, which is one of the correct use of a pointer to storage that has not been yet reused. Similarly p is obtained from &c before the new int is constructed. Its definition could perhaps be moved to after the destruction of c, depending on how deep the implementation magic is, but certainly not after the int construction: paragraph 7 would apply and this is not one of the allowed situations. The construction of the short object also relies on p becoming a pointer to storage.

Now, because int and short are trivial types, I don't have to use the explicit calls to destructors. I don't need the explicit calls to the constructors, either (that is to say, the calls to the usual, Standard placement new declared in <new>). From 3.8 Object lifetime [basic.life]:

1 [...] The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and
  • if the object has non-trivial initialization, its initialization is complete.

The lifetime of an object of type T ends when:

  • if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
  • the storage which the object occupies is reused or released.

This means that I can rewrite the code such that, after folding the intermediate variable q, I end up with the original snippet.

Do note that p cannot be folded away. That is to say, the following is defintively incorrect:

alignas(alignment) char c[sizeof(int)];
*magic_cast<int*>(&c) = 42;
*magic_cast<short*>(&c) = 42;

If we assume that an int object is (trivially) constructed with the second line, then that must mean &c becomes a pointer to storage that has been reused. Thus the third line is incorrect -- although due to 3.8/5-7 and not due to aliasing rules strictly speaking.

If we don't assume that, then the second line is a violation of aliasing rules: we're reading what is actually a char c[sizeof(int)] object through a glvalue of type int, which is not one of the allowed exception. By comparison, *magic_cast<unsigned char>(&c) = 42; would be fine (we would assume a short object is trivially constructed on the third line).

Just like Alf, I would also recommend that you explicitly make use of the Standard placement new when using storage. Skipping destruction for trivial types is fine, but when encountering *some_magic_pointer = foo; you're very much likely facing either a violation of 3.8/5-7 (no matter how magically that pointer was obtained) or of the aliasing rules. This means storing the result of the new expression, too, since you most likely can't reuse the magic pointer once your object is constructed -- due to 3.8/5-7 again.

Reading the bytes of an object (this means using char or unsigned char) is fine however, and you don't even to use reinterpret_cast or anything magic at all. static_cast via cv void* is arguably fine for the job (although I do feel like the Standard could use some better wording there).

Winstead answered 27/9, 2012 at 7:2 Comment(8)
Thanks for the comprehensive overview.Outshoot
Why is it possible to use void* as an alias? (It's not in the list of authorized types.)Fefeal
@AlexandreHamez Note that void is incomplete. So you can have a void* that hold the same address as another pointer (one of the meanings of aliasing), but it’s okay because you cannot read from it anyway—and the so-called aliasing rules of C++ are concerned with reading. And in fact passing around address values, in any guise, cannot on its own run afoul of aliasing rules (but there are plenty of other rules…). Does that make sense to you? (I would improve this answer if you feel some parts aren’t clear enough.)Winstead
@LucDanton Thank you, it's more clear now. I didn't realize that reading from a pointer was the key. If I understand it correctly, it means that as long as I'm not reading from a pointer which may have an incorrect type (regarding aliasing rules), then everything's fine. I thought creating a pointer with an incorrect type was sufficient to break aliasing.Fefeal
@AlexandreHamez That’s exactly it.Winstead
In your "definitively incorrect" presentation, your argument relies on "reading what is actually a char c[sizeof(int)] object through a glvalue of type int" -- except you are not reading it at all, you are storing a value which definitely does have type int to that location.Stretch
@BenVoigt I see your point, although I still think the example is correct in spirit. If no int object is assumed to live at the location, the assignment is bogus. I'm not sure what's the right way to put it though, 'reading' (my words) / 'accessing the stored value' (the standardese) may not quite accurately describe what is going on during assignment.Winstead
Man. This answer thoroughly needa a TL;DR wrt. to the actual question title! Still upvoted. Nice reasoning.Polygamist
S
6

This too:

// valid: char -> type
alignas(int) char c[sizeof(int)];
int * i = reinterpret_cast<int*>(c);

That is not correct. The aliasing rules state under which circumstances it is legal/illegal to access an object through an lvalue of a different type. There is an specific rule that says that you can access any object through a pointer of type char or unsigned char, so the first case is correct. That is, A => B does not necessarily mean B => A. You can access an int through a pointer to char, but you cannot access a char through a pointer to int.


For the benefit of Alf:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non- static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.
Sponge answered 27/9, 2012 at 2:9 Comment(30)
-1 In your opinion, why does the language have placement new?Indevout
@Cheersandhth.-Alf: placement new passes a buffer (void*) to the allocator function. The allocator function then uses that buffer to create an object. Note that according to the lifetime rules, at that point in time the char objects in the buffer end their lifetime. That is not aliasing. new (c) int(5) creates an integer of value 5 in the buffer. The buffer is now an int. reinterpret_cast<int*>(c) = 5 aliases the chars in the buffer as if they were an int. Unless you are claiming that reinterpret_cast<int*>(c) and new (c) int are equivalent...Agentival
you're claiming that a runtime action changes the static type of a variable. that's balderdash.Indevout
@Cheersandhth.-Alf: I never claimed that, the aliasing rules (again, take the time to read §3.10/10) don't deal with the static, but the dynamic type of an object. Again, if I am wrong, correct me --with a quote from the standard, not just with opinions.Agentival
"That is not correct" ... "You can access an int through a pointer to char" If the latter is true, then why is it not correct?Plerre
@ildjarn: You can access an int through a pointer to char, but you cannot access a char through a pointer to int. In the code above the objects in memory are char, and the reinterpret_cast<int*> creates an alias of type int*. You cannot use an int* to access a (or multiple) char objects.Agentival
Ah, understood now. (I do find the particular wording in your answer confusing.)Plerre
@David: in your answer you wrote "you cannot access a char [array] through a pointer to int". You are now yourself claiming that this statement is incorrect, when the char array dynamically contains the representation of an int. You know, claiming that your own statement is incorrect is ludicrous as an argument in favor of its correctness.Indevout
@Cheersandhth.-Alf: You added the [array] yourself, I didn't. If you want me to completely spell it out: you cannot access a char object through a pointer to int The point is that in the code, the char array holds char objects, and you cannot access them (the objects) through a pointer to int. If there was a placement new in the code, then the int would have ended the lifetime of the char objects according to the lifetime rules as the memory is reused for a different object, and at that point you would be accessing an int object stored in a char array.Agentival
@Cheersandhth.-Alf: Again, your opinion vs. the wording of the standard. Unless I have misinterpreted them, in which case, please do tell me what the standard intends, provide a quote that contradicts what I said. I will be the first to remove the answer if it is wrong. BTW, I find your last comment offensive. If you have facts do tell, if not at least avoid being rude. What in the quote of the standard above is not clear? (That would be a constructive thing to do, rather than trolling)Agentival
@David: It's your opinion versus your opinion, since your opinion (about what is correct interpretation of the standard) is self-contradictory. Normally, you should be able to conclude from a self-contradiction that your opinion is wrong. The ad hominem accusation now of rudeness, following straight on the heels of a claim that your own statement in your answer was meaningless instead of wrong, itself following a ludicrous claim that your own statement was incorrect and therefore had to be correct, well it indicates that I may be unable to help enlighten you on this matter.Indevout
+1, I do believe the only way to change the dynamic type of the object stored at some location (storage occupied by the object) is by reusing the storage (ending the lifetime of the previous object occupying that storage and beginning the lifetime of the new object). The only way to reuse storage this way is by placement new. This last part is unfortunately not spelled out in the standard (or at least I couldn't find it), but all examples of reuse in the standard refer to placement new.Corrosive
@Cheersandhth.-Alf: You must have misunderstood what I said, since there is no contradiction at all from the answer to the very last comment. But instead of trying to argument your position you are just seeking excuses. I am yet to find any argument on your side, just excuses: I may be unable to help enlighten you on this matter. Simple question (for the third time): Does the quote above determine that the code in the question is correct or incorrect? This is a simple yes/no with the addition of what entry in the list supports your claim if you consider it valid code.Agentival
@Cheersandhth.-Alf: The fact is, that C++ is a dynamic language by your definition -- that is, it is a language where the dynamic type of an object has an effect (notice the repeated use of the term "dynamic type" throughout the standard). In contrast to say, Python, if you attempt to access an object through the incorrect dynamic type, your code has undefined behaviour (rather than producing a runtime error), even so, the concept of "dynamic type" is required to reason about a C++ program.Colene
@Mankarse: The debate here was about David's logically impossible claim that «"you cannot access a char through a pointer to int"». And that all hinged on the standard not providing a single concrete example, but rather instead using memcpy (IIRC) for its example. In this context the term “dynamic type”, used by the Holy Standard, refers to an original type of an object, the type used to create the bits in memory, and it does not refer to runtime type information as in e.g. Python. That said I do not know if this supports your view or contradicts it. It is very unclear what you mean.Indevout
@Mankarse: Whether you were expressing disagreement or agreement, it may help you to know about the wider meta-context, which is whether the g++ compiler is reasonable and practical, and whether the term “strict aliasing” is an invention of that compiler's enthusiast, or is defined by the standard (it's not). I believe that that not-directly-mentioned aspect is why there's so much heat in this. In my experience, whenever that compiler is potentially put in a bad light (or for that matter, whenever e.g. MSVC, or Python is potentially put in a bad light), fan boys rush to the rescue.Indevout
@Cheersandhth.-Alf: It's definitely true that there are parts of the C++ standard that are hideously indirect or even just ambiguous (even for very basic things such as the lexical grammar), I guess it's bound to cause disagreement. Even so, I don't see anything illogical about the claim that to use an array of char as an int, you must first construct an int in it (with, e.g. placement new).Colene
That said, it does seem that [basic.life] has several possible interpretations, including one in which the lifetime of the int starts as soon as the appropriately sized and aligned char array has been created (and so the int's lifetime would start before any placement new expression).Colene
@Mankarse: The problem with that interpretation is that it means that a raw piece of memory (char [4]) would not have one type but an infinite number of types: every possible POD occupying 4 or less characters of size. This clashes directly with the standard use of the type (single) when referring to an object. The language clearly documents that new is used to create an object, and that it has two steps: allocation and initialization. The lifetime starting with the allocation means that even if no initialization/construction is evaluated, the object does exist.Agentival
@DavidRodríguez-dribeas: you're onto something: you have found an inconsistency, as you say it “clashes”, in your interpretation of things.Indevout
@Mankarse: David's assertion in this answer does not even depend on whether the bit-pattern is valid for the resulting referred to object, it's just a flat out “you cannot access a char through a pointer to int”. That assertion is incompatible with your view “I don't see anything illogical about the claim that to use an array of char as an int, you must first construct an int in it (with, e.g. placement new)”, and it's incompatible with his own comments. The confusion about singular form (he uses that about the array) versus plural is just more of that, self-contradiction. It piles up.Indevout
@Cheersandhth.-Alf: I see what you're saying now. The crux of this disagreement seems to be that David claims that a region of memory can only have a single dynamic type at any particular point in the program's execution, while you believe that even though an int may have been constructed in a char[], the char[] still is an array of char objects.Colene
In David's view, once the int is constructed, the memory no longer contains chars, and so the ability to read the original char[] buffer as an array of char is only granted by the fact that it is always legal to read any object as a char[] (regardless of its dynamic type -- that is, through the 'aliasing' rule from §3.10/10). David's view leads to statements such as "you cannot access a char through a pointer to int"; crucially, once the int is constructed in the buffer, the buffer no longer contains char objects (and so you can now access the int).Colene
In your view, the char[] continues to be a sequence of char objects, as well as containing an int, and so you believe that clearly it is possible to access a char through an int.Colene
yes, i think that's it, or a main part of it. ;-) well, except that the assertion in this answer does not depend on whether an int has been constructed there or not, at all.Indevout
@Cheersandhth.-Alf: Yes, but if an int has been constructed in some region of memory, then that memory no longer contains a char. Instead, the char is just an alternative (and legal) way of accessing the int stored in the region of memory. (at least, according to David's interpretation of the standard).Colene
Such a view could more easily and clearly disagreed with, yes. E.g. the possibility & practical necessity of accessing objects that don't exist. Unfortunately that view has only been expressed in comments, and it flatly contradicts the assertion in the answer.Indevout
@Mankarse: Except that at the point in time that you use placement new with the int, the standard clearly says that the char objects cease to exist: The lifetime of an object of type T ends when: [...] the storage which the object occupies is reused or released. Other parts may be up for interpretation, but that rule is quite clear, is it not? You can still access (read) it through a char* as the standard provides that special guarantee, but attempting to write through char* is a violation of the aliasing rules (name it strict aliasing rules or whatever you want)Agentival
The answer is built on top of the assumption that if you are calling it an array of char is because it is such a beast, and not an int built on top of that. But all this is digressing from the question: is it legal in the standard? No. Is there any doubt as of whether it is legal? Is there any doubt that after constructing an int the char object ceases to exist? Is there any doubt that the aliasing rules allow reading a char through an int*?Agentival
@DavidRodríguez-dribeas: To be clear, I agree with your interpretation.Colene
I
2

Regarding the validity of …

alignas(int) char c[sizeof(int)];
int * i = reinterpret_cast<int*>(c);

The reinterpret_cast itself is OK or not, in the sense of producing a useful pointer value, depending on the compiler. And in this example the result isn't used, in particular, the character array isn't accessed. So there is not much more that can be said about the example as-is: it just depends.

But let's consider an extended version that does touch on the aliasing rules:

void foo( char* );

alignas(int) char c[sizeof( int )];

foo( c );
int* p = reinterpret_cast<int*>( c );
cout << *p << endl;

And let's only consider the case where the compiler guarantees a useful pointer value, one that would place the pointee in the same bytes of memory (the reason that this depends on the compiler is that the standard, in §5.2.10/7, only guarantees it for pointer conversions where the types are alignment-compatible, and otherwise leave it as "unspecified" (but then, the whole of §5.2.10 is somewhat inconsistent with §9.2/18).

Now, one interpretation of the standard's §3.10/10, the so called "strict aliasing" clause (but note that the standard does not ever use the term "strict aliasing"),

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non- static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

is that, as it itself says, concerns the dynamic type of the object residing in the c bytes.

With that interpretation, the read operation on *p is OK if foo has placed an int object there, and otherwise not. So in this case, a char array is accessed via an int* pointer. And nobody is in any doubt that the other way is valid: even though foo may have placed an int object in those bytes, you can freely access that object as a sequence of char values, by the last dash of §3.10/10.

So with this (usual) interpretation, after foo has placed an int there, we can access it as char objects, so at least one char object exists within the memory region named c; and we can access it as int, so at least that one int exists there also; and so David’s assertion in another answer that char objects cannot be accessed as int, is incompatible with this usual interpretation.

David's assertion is also incompatible with the most common use of placement new.

Regarding what other possible interpretations there are, that perhaps could be compatible with David's assertion, well, I can't think of any that make sense.

So in conclusion, as far as the Holy Standard is concerned, merely casting oneself a T* pointer to the array is practically useful or not depending on the compiler, and accessing the pointed to could-be-value is valid or not depending on what's present. In particular, think of a trap representation of int: you would not want that blowing up on you, if the bitpattern happened to be that. So to be safe you have to know what's in there, the bits, and as the call to foo above illustrates the compiler can in general not know that, like, the g++ compiler's strict alignment-based optimizer can in general not know that…

Indevout answered 27/9, 2012 at 5:46 Comment(9)
You have not understood what I said. My assertion is that a char object cannot be accessed through an int pointer. Your assertion is that once you replace the char object with an int object through placement new you can access the int object through an int pointer. You are not accessing a char through the int pointer, you are accessing an int that was created in the same memory location previously held by (multiple) char objects. Learn to read.Agentival
@David: when u say "learn to read", that is a good idea. it seems that you're suffering from some misconception that c++ is like python (say), a dynamically typed language. it's not: those bytes are not endowed with associated dynamic type information, and furthermore, accessing the char objects means that they're there (you can't very well access that which isn't there). in short your logic is completely lacking. maybe due to a fundamental misconception as i've now outlined, and i really hope that that's it.Indevout
So, is your claim that this is correct or incorrect (assume 32bit int): float f; int* p = new (&f) int(1); cout << *p;. And if it is incorrect, why? Note that I did not use dynamic in the sense of holding RTTI information, but as the actual type of the object in the memory location. Is there a float object after placement new? (If you think there is, read the object lifetime chapter in the standard) Is it legal to do cout << f after the placement new? What you fail to see is that you are saying the same thing I said, but failing to see they are the same thing.Agentival
@david: inventing ever more rube goldberg-like perpetuum mobile constructions is not going to get me to even look at it, because (1) the laws of physics say that no matter the contorted inner logic it's wrong, and (2) i know that you know that.Indevout
Your last comment says quite a lot about you. I am sorry that you are like that but I cannot make you a better person.Agentival
@david: i understand why you go to ad hominem again.Indevout
:) Your last comment is enlightnening. You know what ad hominem means right? It means avoiding the argument and rather focus on a personal defect. My last comment was not avoiding the argument, you already did that before. That comment is just an statement of truth (opinion if you like). You are the one avoiding to argue on the example I provided and claiming that you can read my mind (with limited success, to be honest.. that you know that is so off target) So, no, you don't understand why [I] go to ad hominem, as that premise is false.Agentival
I don't really believe that people downvote to support another answer, more so considering that I did not get equivalent up votes. On the other hand, there is someone that has up voted some of the comments. Go figure.Agentival
@DavidRodríguez-dribeas this discussion was sad man. I'm with Alf here. Shouting "learn to read" is sure ad hominen, it doesn't make part of a healthy discourse. Also asking the user to read your mind when you assert that simply the cast snippet is incorrect, while it's actually valid, and Alf's answer serves to avoid that confusion, for then telling people "learn to read"? Maybe we know how to read but you didn't know how to write at the time.Uncinate

© 2022 - 2024 — McMap. All rights reserved.