C++11 breaking change from N3778 regarding placement new and

253 views
Skip to first unread message

Tom Honermann

unread,
Sep 5, 2014, 1:23:20 PM9/5/14
to std-dis...@isocpp.org
N3778 [1] introduced [expr.new] 5.3.4p20 to C++11:

> A declaration of a placement deallocation function matches the
> declaration of a placement allocation function if it has the same
> number of parameters and, after parameter transformations (8.3.5),
> all parameter types except the first are identical. Any non-placement
> deallocation function matches a non-placement allocation function. If
> the lookup finds a single matching deallocation function, that
> function will be called; otherwise, no deallocation function will be
> called. If the lookup finds the two-parameter form of a usual
> deallocation function (3.7.4.2) and that function, considered as a
> placement deallocation function, would have been selected as a match
> for the allocation function, the program is ill-formed. [ Example:
> struct S {
> // Placement allocation function:
> static void* operator new(std::size_t, std::size_t);
> // Usual (non-placement) deallocation function:
> static void operator delete(void*, std::size_t);
> };
>
> S* p = new (0) S; // ill-formed: non-placement deallocation
function matches
> // placement allocation function
> — end example ]

This is a breaking change. The example code compiles successfully as
C++03, but errors in C++11 using Clang 3.5.

$ cat t.cpp
#include <cstddef>

struct S {
void* operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t);
};

S* p = new (0) S;

$ clang++ -c -std=c++03 t.cpp
<no error>

$ clang++ -c -std=c++11 t.cpp
t.cpp:8:8: error: 'new' expression with placement arguments refers to
non-placement 'operator delete'
S* p = new (0) S;
^ ~
t.cpp:5:10: note: 'operator delete' declared here
void operator delete(void*, std::size_t);
^
1 error generated.

Recent gcc releases do not appear to enforce 5.3.4p20. I tried
compiling the test with gcc 4.8 and a branch of 4.10, both with
-std=c++11, and both succeeded.

I don't see any mention of this incompatibility under C++11 [diff.cpp03]
C.2, nor have I found a DR for it.

It isn't clear to me why the rule was added. I presume this is an
intentional change that probably should have been documented in C.2.
Anyone know more about this?

Tom.

[1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3778.html

Richard Smith

unread,
Sep 5, 2014, 4:51:17 PM9/5/14
to std-dis...@isocpp.org
On Fri, Sep 5, 2014 at 10:23 AM, Tom Honermann <thone...@coverity.com> wrote:
N3778 [1] introduced [expr.new] 5.3.4p20 to C++11:

> A declaration of a placement deallocation function matches the
> declaration of a placement allocation function if it has the same
> number of parameters and, after parameter transformations (8.3.5),
> all parameter types except the first are identical. Any non-placement
> deallocation function matches a non-placement allocation function. If
> the lookup finds a single matching deallocation function, that
> function will be called; otherwise, no deallocation function will be
> called. If the lookup finds the two-parameter form of a usual
> deallocation function (3.7.4.2) and that function, considered as a
> placement deallocation function, would have been selected as a match
> for the allocation function, the program is ill-formed. [ Example:
>   struct S {
>     // Placement allocation function:
>     static void* operator new(std::size_t, std::size_t);
>     // Usual (non-placement) deallocation function:
>     static void operator delete(void*, std::size_t);
>   };
>
>   S* p = new (0) S; // ill-formed: non-placement deallocation function matches
>                     // placement allocation function
> — end example ]

That's not correct. The wording was added by DR429:


and N3778 is not part of C++11.

This is a breaking change.

It's a bug fix. Consider:

struct S {
    S() { throw 0; }
    void* operator new(std::size_t, std::size_t);
    void operator delete(void*, std::size_t);
};

// allocates with placement new, deallocates (on exception) with S::operator delete(p, 0);
S* p = new (0) S;

// allocates with placement new, deallocates (on exception) with S::operator delete(p, sizeof(S));
S *q = new S;

This is clearly bad news. The 'operator delete(void *, size_t)' signature is reserved as a non-placement deallocation function, so it doesn't make sense to have a placement allocation function that matches it. (In C++14, the same applies at namespace scope too, due to N3778.)

Tom Honermann

unread,
Sep 5, 2014, 6:03:48 PM9/5/14
to std-dis...@isocpp.org
On 09/05/2014 04:51 PM, Richard Smith wrote:
> On Fri, Sep 5, 2014 at 10:23 AM, Tom Honermann <thone...@coverity.com
> <mailto:thone...@coverity.com>> wrote:
>
> N3778 [1] introduced [expr.new] 5.3.4p20 to C++11:
...
> That's not correct. The wording was added by DR429:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#429

Ah, thanks for the correction and the link.

> and N3778 is not part of C++11.

Ya know, you'd think it might have occurred to me that a paper written
in 2013 is quite unlikely to have been incorporated in C++11... Bah.

> It's a bug fix. Consider:
>
> struct S {
> S() { throw 0; }
> void* operator new(std::size_t, std::size_t);
> void operator delete(void*, std::size_t);
> };
>
> // allocates with placement new, deallocates (on exception) with
> S::operator delete(p, 0);
> S* p = new (0) S;
>
> // allocates with placement new, deallocates (on exception) with
> S::operator delete(p, sizeof(S));
> S *q = new S;
>
> This is clearly bad news. The 'operator delete(void *, size_t)'
> signature is reserved as a non-placement deallocation function, so it
> doesn't make sense to have a placement allocation function that matches
> it. (In C++14, the same applies at namespace scope too, due to N3778.)

Thank you for the clear explanation, makes perfect sense.

Tom.
Reply all
Reply to author
Forward
0 new messages