为什么noncept说明符不在声明的方法范围内?

时间:2016-03-07 23:15:26

标签: c++ c++11 noexcept

尝试设计一些无异常的类,我有一个类似于此的继承结构,但我发现 noexcept 说明符在使用成员函数作为说明符时几乎没有帮助不属于函数范围内。

class Base
{
protected:
    Base() noexcept {}
};

class Derived : public Base
{
public:
    // error: 'Base::Base()' is protected
    Derived() noexcept(noexcept(Base{})) : Base{} {}

    // error: 'foo' was not declared in this scope
    Derived(int) noexcept(noexcept(foo())) {}

    // error: invalid use of 'this' at top level
    Derived(float) noexcept(noexcept(this->foo())) {}

    void foo() noexcept {}
};

Demo

这可能是C ++ 17中正在改进的东西吗?试图搜索这个没有产生相关结果。现在我已经辞职了一些非常丑陋(可能不正确)的尝试,例如noexcept(noexcept(static_cast<Derived*>(nullptr)->foo())),但是这对基类构造函数的情况没有帮助,这是受保护的。

目前是否可以声明一个引用受保护的基类方法的noexcept说明符? noexcept(auto) 可能相关,但当然还不可能。我是否忽略了允许我包含此说明符的任何其他内容,或者在这种情况下我是否只需忽略它?

2 个答案:

答案 0 :(得分:2)

你可以使用一个表达式解决它,其中Base构造函数在这样的范围内:

struct test_base : public Base {};

Derived() noexcept(noexcept(test_base())) : Base() {}

我认为您无法直接使用Base()的原因是related to this question

  

受保护的访问说明符的工作方式,它允许派生类   B只有在那时才能访问基类A的对象的内容   A类的对象是B类的子对象。这意味着唯一的对象   您可以在代码中执行的操作是访问A的内容   B:你可以通过类型B *(或者a)的指针访问A的成员   B&amp;)类型的参考。但是你无法访问相同的成员   指针类型A *(或参考A&amp;)。

就像你有这样的成员函数一样:

void badfunc()
{
    B b;
}

您尝试直接使用Base的构造函数,而不是通过 Derived。在构造函数初始化列表中初始化基类时,这是一个特殊的上下文,允许您调用构造函数,因为您在初始化Derived时执行了此操作。

答案 1 :(得分:1)

这真的是多个问题。

关于this ...

根据我的理解,使用this 假设完全是多余的,但编译器对C ++ 11的支持并不完全通用。根据C ++ 11标准,这应该可行:

struct Base {
    void func() noexcept;
};
struct Derived() {
    void func() noexcept(noexcept(Base::func())) {}
};

请注意base_func()是一个非静态成员函数,但因为它出现在&#34;未评估的操作数&#34;没关系。从n3337秒4.1.1:

  

表示非静态数据成员或类的非静态成员函数的 id-expression 只能使用:

     

...

     
      
  • 如果 id-expression 表示非静态数据成员,并且它出现在未评估的操作数中。
  •   

但是,有些编译器不支持这一点。然后,您被迫使用std::declval

#include <utility>
struct Base {
    void func() noexcept;
};
struct Derived() {
    void func() noexcept(noexcept(std::declval<Base>().func())) {}
};

关于辅助功能......

我阅读了标准的相关部分,关于&#34;未评估的操作数&#34;和&#34;会员访问控制&#34;我得出结论,标准有点含糊不清。它提到protected名称只能由成员,朋友和派生类使用。问题是,未经评估的操作数是否会使用&#34;出现在其中的成员名称。他们当然不会使用使用成员名称,如果没有提供定义,甚至可以使用成员名称,这种含糊不清就是“使用”这个词的原因。 #34;甚至存在!例如,

int f(); // No definition anywhere in program
int x = sizeof(f()); // Completely valid, even without definition of f

struct X {
    X() = delete; // Cannot use this constructor
};
int xsize = sizeof(X{}); // Completely valid

即使有点不清楚,我也很难想象C ++委员会本来打算让你在未评估的操作数中使用已删除的成员函数,但不能访问不可访问的成员函数。但是,我不确定。

请注意,上面的代码与GCC和Clang一起编译时没有错误。但是,以下代码不是:

class X {
    X(){}
};
class Y {
    Y() = delete;
};
bool xokay = noexcept(X{}); // Error!
bool yokay = noexcept(Y{}); // Ok

GCC和Clang都接受Y而不是X,这似乎有点怪异至少可以说。 Clang接受以下代码但不接受GCC,使用std::declval无效:

class Base {
protected:
    void func();
};
class Derived : public Base {
    // Clang accepts this, GCC does not.
    void func2() noexcept(noexcept(Base::func())) {}
};

多么糟糕。

结论

这里的结论是,似乎存在大量的不一致性,以及当前编译器和C ++ 11规范之间的大量差距。