确定抽象基类的构造函数是否为 noexcept
Determine whether a constructor of an abstract base class is noexcept?
在 C++11 及更高版本中,如何确定抽象基类的构造函数是否noexcept
?以下方法不起作用:
#include <new>
#include <type_traits>
#include <utility>
struct Base { Base() noexcept; virtual int f() = 0; };
// static assertion fails, because !std::is_constructible<Base>::value:
static_assert(std::is_nothrow_constructible<Base>::value, "");
// static assertion fails, because !std::is_constructible<Base>::value:
static_assert(std::is_nothrow_default_constructible<Base>::value, "");
// invalid cast to abstract class type 'Base':
static_assert(noexcept(Base()), "");
// invalid new-expression of abstract class type 'Base'
static_assert(noexcept(new (std::declval<void *>()) Base()), "");
// cannot call constructor 'Base::Base' directly:
static_assert(noexcept(Base::Base()), "");
// invalid use of 'Base::Base':
static_assert(noexcept(std::declval<Base &>().Base()), "");
一个简单的用法是:
int g() noexcept;
struct Derived: Base {
template <typename ... Args>
Derived(Args && ... args)
noexcept(noexcept(Base(std::forward<Args>(args)...)))
: Base(std::forward<Args>(args)...)
, m_f(g())
{}
int f() override;
int m_f;
};
关于如何归档它的任何想法,或者是否可以在不修改抽象基类的情况下进行任何想法?
PS:也欢迎任何对ISO C++缺陷报告或正在进行的工作的引用。
编辑:正如两次指出的那样,默认Derived
构造函数具有= default
会使noexcept
被继承。但这并不能解决一般情况的问题。
[更新:值得跳到编辑部分]
好的,我找到了一个解决方案,即使由于 GCC 中的错误,它无法与所有编译器一起编译(有关更多详细信息,请参阅此问题)。
该解决方案基于继承的构造函数和解析函数调用的方式。
请考虑以下示例:
#include <utility>
#include <iostream>
struct B {
B(int y) noexcept: x{y} { }
virtual void f() = 0;
int x;
};
struct D: public B {
private:
using B::B;
public:
template<typename... Args>
D(Args... args)
noexcept(noexcept(D{std::forward<Args>(args)...}))
: B{std::forward<Args>(args)...}
{ }
void f() override { std::cout << x << std::endl; }
};
int main() {
B *b = new D{42};
b->f();
}
我想这很清楚。
无论如何,如果您发现某些内容需要更多详细信息,请告诉我,我很乐意更新答案。
基本思想是,我们可以直接从基类及其构造函数继承noexcept
定义,这样我们就不必再在noexcept
语句中引用该类。
在这里,您可以看到上面提到的工作示例。
[编辑]
从注释中可以看出,如果基类的构造函数和派生类的构造函数具有相同的签名,则该示例会遇到问题。
感谢Piotr Skotnicki指出这一点。
我将提及这些注释,并将复制并粘贴建议的代码(并在需要时提及作者)。
首先,在这里我们可以看到该示例没有按预期工作(感谢 Piotr Skotnicki 的链接)。
代码与之前发布的代码几乎相同,因此不值得在此处复制和粘贴。
此外,来自同一作者,它遵循一个示例,该示例显示相同的解决方案在某些情况下按预期工作(有关更多详细信息,请参阅注释):
#include <utility>
#include <iostream>
struct B {
B(int y) noexcept: x{y}
{
std::cout << "B: Am I actually called?n";
}
virtual void f() = 0;
int x;
};
struct D: private B {
private:
using B::B;
public:
template<typename... Args>
D(int a, Args&&... args)
noexcept(noexcept(D{std::forward<Args>(args)...}))
: B{std::forward<Args>(args)...}
{
std::cout << "D: Am I actually called?n";
}
void f() override { std::cout << x << std::endl; }
};
int main()
{
D* d = new D{71, 42};
(void)d;
}
此外,我提出了一种更具侵入性的替代解决方案,并且基于std::allocator_arg_t
所代表的想法,这也是Piotr Skotnicki在评论中提出的相同:
如果派生类的构造函数在重载解析中获胜就足够了。
它遵循此处提到的代码:
#include <utility>
#include <iostream>
struct B {
B(int y) noexcept: x{y}
{
std::cout << "B: Am I actually called?n";
}
virtual void f() = 0;
int x;
};
struct D: public B {
private:
using B::B;
public:
struct D_tag { };
template<typename... Args>
D(D_tag, Args&&... args)
noexcept(noexcept(D{std::forward<Args>(args)...}))
: B{std::forward<Args>(args)...}
{
std::cout << "D: Am I actually called?n";
}
void f() override { std::cout << x << std::endl; }
};
int main()
{
D* d = new D{D::D_tag{}, 42};
(void)d;
}
再次感谢Piotr Skotnicki的帮助和评论,非常感谢。
根据skypjack的回答,一个不需要更改Derived
构造函数签名的更好解决方案是将Base
的模拟子类定义为Derived
的私有类型成员,并在Derived
构造函数noexcept
规范中使用它的构造:
class Derived: Base {
private:
struct MockDerived: Base {
using Base::Base;
// Override all pure virtual methods with dummy implementations:
int f() override; // No definition required
};
public:
template <typename ... Args>
Derived(Args && ... args)
noexcept(noexcept(MockDerived(std::forward<Args>(args)...)))
: Base(std::forward<Args>(args)...)
, m_f(g())
{}
int f() override { return 42; } // Real implementation
int m_f;
};
一个天真但工作的例子是引入一个非虚拟基类并通过using
指令导出其构造函数。下面是一个示例:
#include<utility>
struct BaseBase {
BaseBase() noexcept { }
};
struct Base: public BaseBase {
using BaseBase::BaseBase;
virtual int f() = 0;
};
struct Derived: public Base {
template <typename ... Args>
Derived(Args && ... args)
noexcept(noexcept(BaseBase(std::forward<Args>(args)...)))
: Base(std::forward<Args>(args)...)
{ }
int f() override { }
};
int main() {
Derived d;
d.f();
}
我遇到了同样的问题,我找到的解决方案是实现额外的特征。
你可以在这里看看我的帖子。
- 在提升multi_index容器中,是否定义了"default index"?
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 检查输入是否不是整数或数字
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 在noexcept 规范中是否允许使用"this"?
- 添加noexcept是否会破坏二进制兼容性
- 捕获其中所有异常的C++函数是否"noexcept"?
- 是否可以引入 noreturn noexcept 函数而不是调用 std::terminate
- 如果禁用了例外,是否所有功能都"noexcept"?
- 标记为 noexcept 的函数是否可以内部有例外
- 确定抽象基类的构造函数是否为 noexcept
- “constexpr”功能是否也应该是“noexcept”
- constexpr是否暗示noexcept
- move构造函数是否要求为noexcept
- 如何检测模板参数是否为noexcept函数
- 添加' noexcept(false) '是否对代码有任何好处?
- C++标准是否规定C链接函数为“noexcept”
- 是否有一个自动的noexcept说明符