【问题标题】:constexpr operator overloading issues with using arguments使用参数的 constexpr 运算符重载问题
【发布时间】:2019-01-22 04:10:37
【问题描述】:

我正在创建一个从 std::array 继承的简单类。关键是如果下标运算符用于越界索引,它应该引发编译时错误。但是,我不断收到错误消息。这是简化的代码。

#include <array>

using namespace std;

template<typename type, size_t size>
struct container : array<type,size>
{
    constexpr inline type& operator[](int index) const
    {
        static_assert(index<size,"");
        return ((static_cast<const array<type,size> >(*this))[index]);
    }

    template<class... bracelist>
    constexpr container(bracelist&&... B)
    :array<type,size>{std::forward<bracelist>(B)...}
    {}

    container() = default;
};

int main()
{
    constexpr container<int,4> myarray = {5,6,7,8};
    constexpr int number = myarray[2];
}

它给我的错误是:

main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression

但是,我在 return 语句中使用了“index”,并且注释掉了 static_assert 使其工作正常。如果 index 不是常量表达式,我不能在 static_cast 之后的 std::array 的下标运算符中使用它吗?我是使用 constexpr 功能的新手,因此将不胜感激。谢谢。

注意:我知道 std::array 的 constexpr 下标运算符已经做到了这一点,我只是想知道如何做到这一点以备将来使用。谢谢。

【问题讨论】:

  • 不幸的是,这不起作用。你可以做return index &gt;= size ? static_cast&lt;const array&lt;type,size&gt; &gt;(*this)[index] : throw std::runtime_error("");
  • 你在使用 C++11 吗? constexpr inline type &amp; 应该是 constexpr inline type const &amp;static_cast&lt;const array&lt;type,size&gt; &gt; 应该是 static_cast&lt;const array&lt;type,size&gt; &amp; &gt; 并且引入类型别名会更好。

标签: c++ arrays constexpr compile-time stdarray


【解决方案1】:

constexpr 函数有 2 个非常有用的特性,但它们之间的相互作用并不总是得到充分理解。

  • 在 constexpr 上下文中,它们仅评估为 constexpr 参数采用的代码路径。

  • 在非 constexpr 上下文中,它们的行为与常规函数完全相同。

这意味着我们可以使用异常来发挥很大的作用。

由于在 constexpr 上下文中,如果采用异常路径,这是编译器错误(在 constexpr 上下文中不允许抛出)。您会在编译器的错误输出中看到“异常”。

示例:

#include <array>
#include <stdexcept>

template<typename type, std::size_t size>
struct container : std::array<type,size>
{
    constexpr auto operator[](std::size_t index) const
    -> type const&
    {
        if (index < size)
            return static_cast<const std::array<type,size>>(*this)[index];
        else
            throw std::out_of_range("index out of range" + std::to_string(index));
    }

    template<class... bracelist>
    constexpr container(bracelist&&... B)
    : std::array<type,size>{std::forward<bracelist>(B)...}
    {}

    container() = default;

};

int main()
{
    constexpr container<int,4> myarray = {5,6,7,8};
    constexpr int number = myarray[4];
}

示例输出:

main.cpp: In function 'int main()':
main.cpp:28:37:   in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
             throw std::out_of_range("index out of range" + std::to_string(index));

这种方法实际上比 static_assert 更通用,因为它在编译和运行时都有效。

【讨论】:

  • 你可能想抛出 std::out_of_range 以便它可以被捕获为 std::exception const&amp;
  • @HenriMenke 是的,当然。我会解决的。我很懒
【解决方案2】:

您必须记住的是constexpr 函数可以在运行时使用非 constexpr 参数调用。 constexpr 表示函数在编译时评估的表达式(例如,另一个 constexpr 或模板参数)中可用,但不是唯一的。 constexpr 函数仍然可以以 classical 方式调用,即在运行时使用运行时变量。这意味着constexpr 函数的参数不能也不是编译时常量。

它不适用于您的情况,但一般来说,如果您知道参数将始终使用编译时间常量调用,那么您可以将其设为模板参数。

constexpr void foo(int a)
{
    static_assert(a != 0); // illegal code because the parameter 
                           // cannot be a compile time constant
}

void test()
{
    int x;
    std::cin >> x;
    foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
    static_assert(N != 0); // legal
}

void test()
{
    int x;
    std::cin >> x;
    foo<x>(); // illegal, x is not a compile time constant


    foo<24>(); // legal
    constexpr int c = 11;
    foo<c>();; // legal
}

【讨论】:

    【解决方案3】:

    这就是std::get&lt;N&gt;(array) 的原因——它是确保以符合语言规则的方式传递“编译时值”的唯一方法。您创建编译时op[] 的尝试无效。您当然可以制作自己的模板访问器,例如 std::get,但有人可能会问为什么不直接使用 std::array

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-03
      • 2019-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多