使用C ++ 14的gcc 6.3出现异常清除错误 [英] exception cleanup error on gcc 6.3 with C++14

查看:63
本文介绍了使用C ++ 14的gcc 6.3出现异常清除错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用gcc 6.3编译我的C ++ 14项目,并且得到:

I am trying to compile C++14 project of mine with gcc 6.3 and I am getting:

hm3/pairblob.cc: In static member function 'static std::unique_ptr<hm3::PairBlob> hm3::PairBlob::create(const hm3::PairBlob*)':
hm3/pairblob.cc:64:44: error: exception cleanup for this placement new selects non-placement operator delete [-fpermissive]
  std::unique_ptr<PairBlob> pair{ new(size) PairBlob };
                                            ^~~~~~~~
In file included from /usr/include/c++/6.3.1/ext/new_allocator.h:33:0,
                 from /usr/include/c++/6.3.1/armv7l-unknown-linux-gnueabihf/bits/c++allocator.h:33,
                 from /usr/include/c++/6.3.1/bits/allocator.h:46,
                 from /usr/include/c++/6.3.1/memory:63,
                 from hm3/pairblob.h:10,
                 from hm3/pairblob.cc:1:
/usr/include/c++/6.3.1/new:125:6: note: 'void operator delete(void*, std::size_t)' is a usual (non-placement) deallocation function in C++14 (or with -fsized-deallocation)
 void operator delete(void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
      ^~~~~~~~

代码相对复杂,但这是有问题的部分:

Code is relatively complicated but this is the part in question:

std::unique_ptr<PairBlob> PairBlob::create(const PairBlob *src){
    if (src == nullptr)
        return {};

    size_t const size = src->getBytes();

    std::unique_ptr<PairBlob> pair{ new(size) PairBlob };

    memcpy(pair.get(), src, size);

    return pair;
}

PairBlob :: create 是静态方法. PairBlob 是一个POD,具有私有默认c-tor,没有d-tor:

This PairBlob::create is a static method. PairBlob is a POD and have private default c-tor and have no d-tor:

struct PairBlob{
private:
    PairBlob() = default;

    static void *operator new(size_t, size_t const size){
        return ::operator new(size);
    }

    static void *operator new(size_t, size_t const size, const std::nothrow_t){
        return ::operator new(size, std::nothrow);
    }

// ...

错误仅在C ++ 14上出现,使用C ++ 11编译没有错误.我目前正在研究32位ARM,但我认为这没有关系.

Error appear only on C++14, compiling with C++11 is without error. I am currently working on 32 bit ARM but I do not think this is related.

更新

我发现如果我注释掉c-tor,代码就可以工作.但是在这种情况下,c-tor是公开的,这不是我想要的.

I found if I comment out the c-tor, code works. But in this case the c-tor is public and this is not what I want.

如果我这样编写代码,则会给出相同的错误

If I do code like this, it does give same error

PairBlob() noexcept = default;

更新

因为我要提供 new 运算符,所以我决定也提供 delete .现在,它可以正确编译了.我确实放了一些 noexcept ,但并不是必需的.

I because I am providing new operators, I decided to provide delete as well. Now it compiles correctly. I did put some noexcept but they are not really required.

struct PairBlob{
private:
    PairBlob() = default;

    static void *operator new(size_t, size_t const size){
        return ::operator new(size);
    }

    static void *operator new(size_t, size_t const size, const std::nothrow_t) noexcept{
        return ::operator new(size, std::nothrow);
    }

public:
    // fixing C++14 error
    static void operator delete(void* memory){
        ::operator delete(memory);
    }

我没有将其发布为答案,因为我仍然不明白为什么gcc无法使用常规"运算符删除.

I am not posting this as answer, because I still do not understand why gcc can not use "normal" operator delete.

推荐答案

简短答案:

从C ++ 14开始,全局函数 void运算符delete(void *,size_t)noexcept 被定义为常规的(非放置) delete 函数,并且因此不再被认为是放置 delete 函数(此行为与 static 成员函数 void T :: operator delete(void *,size_t)noexcept 匹配),这是通常的 delete 函数).这意味着它不能匹配任何放置 new 函数,从而在查找 void * PairBlob :: operator new(size_t,size_t)的同时查找发现它时导致错误匹配.

Short answer:

As of C++14, global function void operator delete(void*, size_t) noexcept is defined as a usual (non-placement) delete function, and thus is no longer considered a placement delete function (this behaviour matches static member function void T::operator delete(void*, size_t) noexcept, which is a usual delete function). This means it can't match any placement new functions, causing the error when lookup discovers it while looking for void* PairBlob::operator new(size_t, size_t)'s match.

添加 void PairBlob :: operator delete(void *)可以防止查找发现上述全局 operator delete()重载,并因此发现位置 new <如果构造函数抛出异常,则/code>不会调用展示位置 delete .

Adding void PairBlob::operator delete(void*) prevents lookup from discovering the aforementioned global operator delete() overload, and thus the placement new won't call a placement delete if the constructor throws.

比较C ++ 11和C ++ 14中 [new.delete.single] 中描述的功能(为简洁起见,省略了除 delete 功能签名之外的所有信息)).

Compare the functions described in [new.delete.single] in C++11 and C++14 (all information other than delete function signatures omitted for brevity).

void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, const std::nothrow_t&) noexcept;

C ++ 14

void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, std::size_t size) noexcept;
void operator delete(void* ptr, const std::nothrow_t&) noexcept;
void operator delete(void* ptr, std::size_t size, const std::nothrow_t&) noexcept;

C ++ 14 [new.delete.single/14] 似乎表明,如果存在, size 必须是隐式传递给通常的 void *运算符new(size_t),称为 new Whatever .

Examination of C++14 [new.delete.single/14] appears to indicate that if present, size must be the value implicitly passed to the usual void* operator new(size_t), when called as new Whatever.

[basic.stc.dynamic/2] 中指定,在这些单对象 delete 函数中,以下内容在全局范围内隐式声明:所有翻译单位:

As specified in [basic.stc.dynamic/2], out of these single-object delete functions, the following are implicitly declared, in global scope, in all translation units:

void operator delete(void*);

C ++ 14

void operator delete(void*) noexcept;
void operator delete(void*, std::size_t) noexcept;


类似地,在 C ++ 14 [basic.stc.dynamic.deallocation/2] 包含以下子句:

具有正好两个参数的全局 operator delete 是常用的释放函数,其中第二个参数的类型为 std :: size_t .

The global operator delete with exactly two parameters, the second of which has type std::size_t, is a usual deallocation function.

C中没有此子句 [basic.stc.dynamic.deallocation/2] 的++ 11 版本.

This clause isn't present in the C++11 version of [basic.stc.dynamic.deallocation/2].

此外, C ++中的脚注3714 版本,指的是 operator delete(void * ptr,size_t)noexcept operator delete [](void * ptr,size_t)noexcept ,其中指出:

Furthermore, footnote 37 in the C++14 version, referring to operator delete(void* ptr, size_t) noexcept and operator delete[](void* ptr, size_t) noexcept, states that:

此解除分配功能禁止使用分配功能 void运算符new(std :: size_t,std :: size_t)作为展示位置分配功能(

This deallocation function precludes use of an allocation function void operator new(std::size_t, std::size_t) as a placement allocation function ([diff.cpp11.basic]).

链接页面指出,此更改可能会破坏有效的C ++ 11代码,该代码依赖于全局函数 void运算符delete(void *,size_t)noexcept 是放置位置 delete 函数,导致程序编译为C ++ 14时格式错误.这与大小相同的 delete 函数作为类成员的行为匹配,其中依赖于 static 成员函数的代码 T :: operator delete(void *,size_t)noexcept是放置 delete 函数的格式不正确.

The linked page notes that this change may break valid C++11 code which relies on global function void operator delete(void*, size_t) noexcept being a placement delete function, causing the program to be ill-formed when compiled as C++14. This matches the sized delete function's behaviour as a class member, where code that relies on static member function void T::operator delete(void*, size_t) noexcept being a placement delete function is ill-formed.

关于 static 成员函数 void T :: operator delete(void *,size_t) [basic.stc.dynamic.deallocation/2]指出,在两个 C ++ 11中 C ++ 14 ,即:

In regards to static member function void T::operator delete(void*, size_t), [basic.stc.dynamic.deallocation/2] states, in both C++11 and C++14, that:

如果类 T 具有名为 operator delete 的成员解除分配函数,且该函数具有正好一个参数,则该函数是通常的解除分配函数.如果类 T 没有声明这种 operator delete ,但确实声明了一个具有确切两个参数的名为 operator delete 的成员释放函数,其中第二个参数的类型为 std :: size_t ,则此函数为通常的释放函数.

If a class T has a member deallocation function named operator delete with exactly one parameter, then that function is a usual deallocation function. If class T does not declare such an operator delete but does declare a member deallocation function named operator delete with exactly two parameters, the second of which has type std::size_t, then this function is a usual deallocation function.

语录取自C ++ 14版本.C ++ 11版本澄清了通常"的意思是非放置",并链接到 [support.types] 以获得 std :: size_t 的定义.C ++ 14版本在到达此子句之前阐明了通常"的含义,并省略了指向 [support.types] 的链接.

Quote taken from C++14 version. The C++11 version clarifies that "usual" means "non-placement", and links to [support.types] for a definition of std::size_t. The C++14 version clarifies the meaning of "usual" before reaching this clause, and omits the link to [support.types].

[expr.new] (具体来说,必须是全局成员或类成员,名称查找规则指出,与 T :: operator new()匹配时,如果没有成员,它将回退到全局 operator delete() s找到运算符delete().[有关此行为的演示,请参见此处.]

[expr.new] (specifically, [expr.new/20] in C++11, and [expr.new/22] in C++14) indicates that for a call to any placement new function, it will try to find a corresponding placement delete function via lookup, which will be called if the constructor throws when using that placement new; an operator delete() will be considered a match if its parameter list matches the operator new()'s parameter list (with the exception of the first parameter, which must always be size_t for operator new() and void* for operator delete()). As deallocation functions must be either global or class members, name lookup rules state that when matching T::operator new(), it will fall back to the global operator delete()s if no member operator delete()s are found. [See here for a demonstration of this behaviour.]

如果通过查找发现了相应的函数,并且该函数是放置 delete 函数,那么一切都很好.如果不是,则表明程序格式错误.擦在其中;由于 PairBlob 最初不包含 operator delete(),因此查找回退到全局范围,并找到 void运算符delete(void *,size_t)(如果有).这是C ++ 11或更早版本中的放置 delete 函数(甚至可能不存在,在这种情况下,请参见下面的段落),但是隐式声明了通常的 delete C ++ 14或更高版本中的函数(因此始终存在),这意味着在从C ++ 11到C ++的过渡期间,依赖于它作为放置 delete 的任何代码都将变得格式错误14.

If a corresponding function is discovered by lookup, and that function is a placement delete function, all is well and good; if it's not, however, then the program is ill-formed. Therein lies the rub; since PairBlob doesn't originally contain an operator delete(), lookup falls back to the global scope, and locates void operator delete(void*, size_t) (if present). This is a placement delete function in C++11 or earlier (and may not even be present, in which case see the below paragraph), but an implicitly declared usual delete function in C++14 or later (and thus always present), meaning that any code which relies on it as a placement delete becomes ill-formed during the transition from C++11 to C++14.

但是,如果通过查找未找到相应的函数,则如果构造函数抛出异常,则不会调用放置 delete 函数.这就是为什么添加 void PairBlob :: operator delete(void *)会消除此错误的原因;当查找到此函数后,查找将停止,因此不会发现全局 operator delete(void *,size_t)noexcept ,因此该程序不会由于尝试使用它而变得格式错误.展示位置 delete 函数.[但是请注意,添加 void PairBlob :: operator delete(void *,size_t)会导致与依赖全局变量相同的错误,因为 static void T :: operator delete(void *,size_t)noexcept 也是常用的删除功能.]

If no corresponding function is discovered by lookup, however, then no placement delete function will be called if the constructor throws. This is why adding void PairBlob::operator delete(void*) removes the error; as lookup stops when it finds this function, the global operator delete(void*, size_t) noexcept will not be discovered, and thus the program won't become ill-formed by trying to use it as a placement delete function. [Note, however, that adding void PairBlob::operator delete(void*, size_t) would cause the same error as relying on the global one, as static void T::operator delete(void*, size_t) noexcept is also a usual delete function.]

GCC 6默认为C ++ 14,导致此问题的原因发生在之前MSVC 2015及更高版本也将捕获此错误,并发出错误C2956.它目前似乎没有相应的MSDN页面,但此错误报告明确引用 [expr.new/22] (尽管编号为5.3.4/22,而不是其名称).但是,令人惊讶的是,Clang无法捕获它,并将继续将 void运算符delete(void *,size_t)noexcept 视为放置 delete .[如果需要,请在此处进行比较.]

GCC 6 defaults to C++14, which has caused this to occur before. MSVC 2015 and later will also catch this, and emit error C2956; it doesn't appear to have a corresponding MSDN page at the moment, but this bug report explicitly refers to [expr.new/22] (albeit by its number, § 5.3.4/22, rather than its name). Surprisingly, however, Clang fails to catch it, and will continue to treat void operator delete(void*, size_t) noexcept as a placement delete. [Compare them here, if you want.]

如上面的GCC链接所述,最好的解决方案是修改代码以使其与C ++ 14兼容,但是使用先前的C ++标准进行编译是一个可行的解决方法.添加成员 operator delete()成员以防止名称查找无法找到全局成员确实可以防止此问题,但是我不确定修改 new 是否会更好代码>.

As mentioned on the GCC link above, the best solution is to modify the code to be compatible with C++14, but compiling with a previous C++ standard is a viable workaround. Adding a member operator delete() that prevents name lookup from finding the global one does indeed prevent this problem, but I'm not sure whether it would be better to modify the placement new instead.

这篇关于使用C ++ 14的gcc 6.3出现异常清除错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆