Constexpr найти для массива с помощью C ++ 17 - PullRequest
0 голосов
/ 30 июня 2018

Я пытаюсь написать функцию поиска constexpr, которая будет возвращать индекс массива std ::, содержащий определенное значение. Представленная ниже функция работает нормально, за исключением случаев, когда содержащийся тип имеет вид 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()?

Я также нашел этот ответ , но пользователь создает класс массива с нуля, и я не хочу этого делать.

1 Ответ

0 голосов
/ 30 июня 2018

Похоже на ошибку компилятора. И f1, и f2 не должны компилироваться одинаково.

Основная проблема заключается в том, что предполагает , что "name1" == "name1" и "name1" != "name2". Стандарт фактически не предоставляет таких гарантий, см. [lex.string] / 16 :

Все ли строковые литералы различны (то есть, хранятся ли в неперекрывающихся объектах) и дают ли последовательные вычисления строкового литерала одинаковые или другой объект не определен.

Даже если предположение, скорее всего, верно, сравнение неуказанных значений внутри constexpr явно не допускается, см. [expr.const] /2.23:

& mdash; оператор отношения ( [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;
}
...