【问题标题】:Static constexpr array of class objects inside the class itself类本身内部的类对象的静态 constexpr 数组
【发布时间】:2018-02-06 14:10:42
【问题描述】:

在 C++ 中是否可以有这样的东西:

struct Foo
{
    int x;
    constexpr Foo(int x) : x(x) {}

    static constexpr Foo table[] =
    {
        Foo(0),
        Foo(1),
        Foo(2),
    };
};

我尝试了几种组合,但都没有奏效。如果 table 不是 Foo 类的一部分,它可以工作,但是我真的希望它成为 Foo 命名空间的一部分。


编辑:

我想要这个的原因是我可以以Foo::table 的身份访问该表。我在命名空间中有几个这样的类,如果我可以通过编写using someNamespace::Foo 导入我正在使用的类,然后以Foo::table 访问表,那真的很方便。如果表在课堂之外,我必须始终通过写someNamespace::fooTable 来访问它。

【问题讨论】:

  • @tobi303 不,因为表是静态的。
  • ups 很抱歉忽略了这一点
  • 它无法工作,因为在解析 table 时,您没有 Foo 的完整定义。要创建Foo 的实例,您需要完整的定义,编译器必须解析整个结构直到结束。
  • @Someprogrammerdude 我知道,编译器错误解释得很好。我想没有办法实现这样的目标。我还编辑了这个问题,以解释为什么我首先想要这样的东西。

标签: c++ arrays static constexpr


【解决方案1】:

compiler error is clear here:

error: invalid use of incomplete type 'struct Foo'
         Foo(0),
              ^
note: definition of 'struct Foo' is not complete until the closing brace
 struct Foo
        ^~~

Foo 被视为"incomplete type",直到到达其定义的右大括号。不完整类型的大小是未知的,因此编译器不知道table 需要多少空间。


这里有一个解决方法:

struct FooTable
{
    constexpr auto operator[](int n) const;
};  

struct Foo
{
    int x;
    constexpr Foo(int x) : x(x) {}

    constexpr static FooTable table{};
};

constexpr auto FooTable::operator[](int n) const
{
    constexpr Foo table[] =
    {
        Foo(0),
        Foo(1),
        Foo(2),
    };

    return table[n];
}

live example on wandbox

用法:

int main()
{
    constexpr auto x = Foo::table[1];
}

如果您不想复制Foo,可以将table 放在“详细信息”namespace 中,然后从FooTable::operator[] - example here 返回const auto&

【讨论】:

  • 问题是我怎样才能实现这样的目标,而不是为什么它不起作用。编译器错误的描述性足以理解原因。
  • 很酷的解决方法:) 但是它没有所需的功能。 operator[] 返回一个副本,这对于编译时间表来说是不可取的。我已经在您的live example 中添加了一些代码来显示这一点。
  • 值得一提的是,最后一个 wandbox 示例只能编译,因为在 C++1z 中保证了复制省略。它在 C++14 中失败。数组初始化器必须调整为不使用复制初始化。
  • @StoryTeller Here is a C++14 工作版。
【解决方案2】:

您可以使用以下技巧,它基本上将表移动到模板包装器,仅当 Foo 的类定义完成时才实例化。

template<typename T>
struct Wrapper
{
    static constexpr T table[] = { T(0), T(1), T(2) };
};

struct Foo : public Wrapper<Foo>
{
    int x;
    constexpr Foo(int x) : x(x) {}
};

不确定在您的情况下这是否真的是一个可接受的解决方法,但它是您如何让您的示例编译和run

如果您想在 Foo 类中指定表条目的初始化值,您可以扩展包装器以获取这些值:

template<typename T, int... Args>
struct Wrapper
{
    static constexpr T table[] = { T(Args)... };
};

struct Foo : public Wrapper<Foo, 0, 1, 2>
{
    int x;
    constexpr Foo(int x) : x(x) {}
};

在这两种情况下,您都可以让您的所有类派生自 Wrapper,而无需定义其他类,因为每个实例化都存在 Wrapper 的静态变量。如果您需要您的条目采用 int 以外的值,您还可以将该类型作为另一个模板参数传递:

template<typename T, typename A, A... Args>
struct Wrapper
{
    static constexpr T table[] = { T(Args)... };
};

struct Foo : public Wrapper<Foo, int, 0, 1, 2>
{
    int x;
    constexpr Foo(int x) : x(x) {}
};

struct Bar : public Wrapper<Bar, char, 'a', 'b', 'c'>
{
    char x;
    constexpr Bar(char x) : x(x) {}
};

向每个构造函数传递多个参数也是可以实现的。在这种情况下,使用std::pair 或其他包装器将它们分组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 2011-06-21
    • 2016-10-15
    相关资源
    最近更新 更多