1

在编写自定义反射库时,我遇到了一个奇怪的编译器行为。但是,我能够使用非常简化的代码重现该问题。这是:

#include <iostream>

class OtherBase{};

class Base{};

/* Used only as a test class to verify if the reflection API works properly*/
class Derived : Base, OtherBase
{
public:
    
    void Printer()
    {
        std::cout << "Derived::Printer() has been called" << std::endl;
    }
};

/*Descriptor class that basically incapsulate the address of Derived::Printer method*/
struct ClassDescriptor
{
    using type = Derived;

    struct FuncDescriptor
    {
        static constexpr const auto member_address{ &type::Printer };
    };
};

int main()
{
    Derived derived;
    auto address{ &Derived::Printer };
    (derived.*address)(); // -> OK it compiles fine using the local variable address
    (derived.*ClassDescriptor::FuncDescriptor::member_address)(); // -> BROKEN using the address from the descriptor class cause fatal error C1001 !
}

在尝试调试此问题时,我注意到:

  1. 仅当Derived具有多重继承时才会发生。
  2. static constexpr const auto member_address{ &type::Printer }如果我与之交换inline static const auto member_address{ &type::Printer }它就可以了。

它只是一个编译器错误,还是我做错了什么?我可以在保留 constexpr 的同时解决这个问题吗?

请注意,我使用的是 MSVC 2017 和编译器版本 19.16.27024.1 所有编译器选项都是默认的,除了 /std:c++17 启用。

我知道将编译器版本更新到最后一个版本(我肯定会这样做)可能会解决这个问题,但现在我想更多地了解这个问题。

4

1 回答 1

0
  1. 关于 C1001,Microsoft Developer Network 建议您删除代码中的一些优化:致命错误 C1001。一旦您确定了导致问​​题的优化,您可以使用 #pragma 仅在该区域禁用该优化:

    // 禁用优化 #pragma optimize( "", off ) ... // 重新启用之前的优化 #pragma optimize( "", on )

此外, Microsoft已发布了针对此问题的修复程序。您可以安装最新版本

  1. constconstexpr

const将对象声明为常量。这意味着保证一旦初始化,该对象的值就不会改变,并且编译器可以利用这一事实进行优化。它还有助于防止程序员编写修改初始化后不打算修改的对象的代码。

constexpr声明一个对象适合在标准所谓的常量表达式中使用。但请注意,这constexpr不是唯一的方法。

当应用于函数时,基本区别是:

const只能用于非静态成员函数,一般不能用于函数。它保证成员函数不会修改任何非静态数据成员。

constexpr可以与成员函数和非成员函数以及构造函数一起使用。它声明了适合在常量表达式中使用的函数。编译器仅在函数满足某些条件(7.1.5/3,4)时才会接受它,最重要的是:

函数体必须是非虚拟的并且非常简单:除了 typedef 和静态断言之外,只return允许使用一条语句。在构造函数的情况下,只允许使用初始化列表、typedef 和静态断言。(但= default= delete允许constexprand asm_ goto_casedefault,try-block,非文字类型变量的定义,静态或线程存储持续时间的变量的定义,不进行初始化的变量的定义。参数和返回类型必须是文字类型(即,一般来说,非常简单的类型,通常是标量或聚合)

const我什么时候可以/应该同时使用两者constexpr

A. 在对象声明中。当两个关键字都引用要声明的同一个对象时,这从来没有必要。constexpr意味着常量。

constexpr const int N = 5;
is the same as

constexpr int N = 5;

但是,请注意,在某些情况下,每个关键字都引用声明的不同部分:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

在这里,NP被声明为地址常量表达式,即本身就是常量表达式的指针。(当通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)这里,两者constexprconst都是必需的:constexpr总是指正在声明的表达式(这里NP),而 const 指的是int(它声明了一个指针-to-const)。删除 const 会使表达式非法(因为 (a) 指向非 const 对象的指针不能是常量表达式,并且 (b)&N实际上是指向常量的指针)。

B. 在成员函数声明中。在 C++11 中,constexpr意味着 const,而在 C++14 和 C++17 中则不是这样。在 C++11 下声明为的成员函数

constexpr void f();
needs to be declared as

constexpr void f() const;

在 C++14 下,以便仍然可以用作const函数。

您可以参考此链接了解更多详情。

于 2020-07-27T02:45:20.443 回答