使用参数的constexpr运算符重载问题

时间:2018-08-15 09:19:18

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

我正在做一个继承自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下标运算符已经做到了,我只想知道如何做以备将来使用。谢谢。

3 个答案:

答案 0 :(得分:2)

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更具通用性,因为它在编译和运行时均有效。

答案 1 :(得分:1)

您要记住的是,constexpr函数可以在运行时使用非constexpr参数进行调用。 constexpr对于一个函数而言,意味着该函数在编译时评估的表达式(例如,另一个constexpr或模板参数)中可以可用,但不能排他地使用。 constexpr函数仍可以以经典的方式调用,即在运行时使用运行时变量。这意味着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
}

答案 2 :(得分:0)

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