首页 > 解决方案 > Consexpr 使用 c++17 查找数组

问题描述

我正在尝试编写一个 constexpr find 函数,该函数将返回包含某个值的 std::array 的索引。下面的函数似乎可以正常工作,除非包含的类型是const char*

#include <array>

constexpr auto name1() {
    return "name1";
}

constexpr auto name2() {
    return "name2";
}

template <class X, class V>
constexpr auto find(X& x, V key) {
    std::size_t i = 0;
    while(i < x.size()) {
        if(x[i] == key) return i;
        ++i;
    }

    return i;
}


int main() {

    constexpr std::array<const char*, 2> x{{name1(), name2()}};
    constexpr auto f1 = find(x, name1()); // this compiles
    constexpr auto f2 = find(x, name2()); // this doesn't...
}

奇怪的是find(x, name1())编译干净但find(x, name2())失败并出现错误:

subexpression not valid in a constant expression
        if(x[i] == key) return i; `

这个表达式在使用时如何工作,name1()但在使用时失败name2()

我也找到了这个答案,但是用户从头开始构建数组类,我不想这样做。

标签: c++arraysc++17constexpr

解决方案


似乎是一个编译器错误。两者都f1应该f2无法以相同的方式编译。

主要问题是它是一个假设"name1" == "name1""name1" != "name2"。该标准实际上没有提供这样的保证,请参阅[lex.string]/16

是否所有字符串文字都是不同的(即,存储在不重叠的对象中)以及字符串文字的连续评估是否产生相同或不同的对象是未指定的。

即使假设最有可能成立,但constexpr明确不允许比较内部未指定的值,请参阅[expr.const]/2.23

— 结果未指定的关系 ( [expr.rel] ) 或相等 ( [expr.eq] ) 运算符;

一种解决方法(也是正确的做法)是不依赖字符串文字的地址,而是比较实际的字符串。例如:

constexpr bool equals(const char* a, const char* b) {
    for (std::size_t i = 0; ; ++i) {
        if (a[i] != b[i]) return false;
        if (a[i] == 0) break;
    }
    return true;
}

template <class X, class V>
constexpr auto find(X& x, V key) {
    std::size_t i = 0;
    while(i < x.size()) {
        if(equals(x[i], key)) return i;
        ++i;
    }

    return i;
}

推荐阅读