这个 constexpr 虚函数技术是否违反了任何 C++11/C++14 规则?

Does this constexpr virtual function technique violate any C++11/C++14 rule?

前几天我在阅读 C++ 文档时注意到,虽然字面量类型不能有虚拟成员,但这并不妨碍它们实现 虚拟成员。或者至少我是这么理解的。

这是我一直在玩的一段代码:

#include <cassert>

// Some forward declarations:

enum class literal_id;
struct literal_base;
struct literal_a;
struct literal_b;

// Now some definitions:

enum class literal_id {
    a, b 
};

struct literal_base {
    virtual literal_id method() const noexcept = 0;
};

struct literal_a : public literal_base {
    constexpr literal_id method() const noexcept final { return literal_id::a; }
    constexpr operator literal_b() const noexcept;
};

struct literal_b : public literal_base {
    constexpr literal_id method() const noexcept final { return literal_id::b; }
    constexpr operator literal_a() const noexcept;
};

constexpr literal_a::operator literal_b() const noexcept { return literal_b(); }
constexpr literal_b::operator literal_a() const noexcept { return literal_a(); }

// Some test methods

literal_id process_literal_base(literal_base const& l) { return l.method(); }
constexpr literal_id process_literal_a(literal_a const& l) { return l.method(); }
constexpr literal_id process_literal_b(literal_b const& l) { return l.method(); }

// Some test variables

constexpr auto a = literal_a();
constexpr auto b = literal_b();

int main() {
    // Compile-time tests, all ok
    static_assert(process_literal_a(b) == literal_id::a, "");
    static_assert(process_literal_b(a) == literal_id::b, "");

    // Runtime tests, all ok
    assert(process_literal_base(a) == literal_id::a);
    assert(process_literal_base(b) == literal_id::b);

    return 0;
}

一些备注:

一些相关的definitions for literal types:

... possibly cv-qualified class type that has all of the following properties:

  • (1) has a trivial destructor. [[ they have (the subclasses of literal_base, I mean) ]]
  • (2) is either
    • (2.1) an aggregate type, [[ not applicable ]]
    • (2.2) a type with at least one constexpr (possibly template) constructor that is not a copy or move constructor, [[ it has, but just because none of the classes have an explicit constructor; but it's easy to achieve ]]
    • (2.3) a closure type (since C++17) [[ not applicable ]]
  • (3) for unions, at least one non-static data member is of non-volatile literal type, [[ not applicable ]]
  • (4) for non-unions, all non-static data members and base classes are of non-volatile literal types. (since C++17) [[ no volatile in the example, and neither should volatile be used in a real application; also, literal_base's subclasses are supposed to be literal types, so this rule must (and can) be applied ]]
  • (5) all non-static data members and base classes are of non-volatile literal types. [[ same as (4), basically ]]

现在一些definitions for constexpr functions:

  • it must not be virtual [[ none of the subclasses have virtual functions; all of them are final, and therefore their locations are known without the need for e.g. vtables ]]
  • (...)

我的假设对吗?有没有我忽略的规范?

[dcl.constexpr]中的规则很清楚:

The definition of a constexpr function shall satisfy the following requirements:
— it shall not be virtual (10.3);

literal_a::methodliteral_b::method 都是 virtual,因为它们都覆盖了 literal_base::method,即 virtual。因此,他们不能是constexpr.,他们是final也没关系。程序格式错误。

真实的,但允许文字类型具有 virtual 成员函数。