Как сравнить string_view с помощью if-constexpt в контексте constexpr

Можно ли сравнить std :: string_view с использованием if constexpr в контексте constexpr? И почему is_hello_2 и is_hello_4 не компилируются, показывая ошибку: «s» не является постоянным выражением

static constexpr bool is_hello_1(auto s) {
  return s == "hello";
}

static constexpr bool is_hello_2(auto s) {
    if constexpr (s == "hello") {
        return true;
    }
    return false;
}

static constexpr auto is_hello_3 = [](auto s) {
    return s == "hello";
};

static constexpr auto is_hello_4 = [](auto s) {
    if constexpr (s == "hello") {
        return true;
    }
    return false;
};

Учитывая основную функцию (https://godbolt.org/z/zEcnb8):

int main(int argc, char **argv) {
    static constexpr const std::string_view s1 ("hello");
    if constexpr (s1 == "hello"){}
    if constexpr (is_hello_1(s1)){}
    // if constexpr (is_hello_2(s1)){} // <- doesn't compile
    if constexpr (is_hello_3(s1)){}
    // if constexpr (is_hello_4(s1)){} // <- doesn't compile
    return 0;
}

Есть ли способ исправить is_hello_2 и is_hello_4?


person Equod    schedule 19.10.2020    source источник
comment
пожалуйста, сосредоточьтесь на одном вопросе на вопрос. Причина, по которой компилятор (ы) по-разному трактует часть, довольно неясна. И, пожалуйста, включите сообщение об ошибке компилятора в вопрос   -  person 463035818_is_not_a_number    schedule 19.10.2020
comment
@ idclev463035818 Вопрос обновлен   -  person Equod    schedule 19.10.2020
comment
godbolt.org/z/Kxec44   -  person Marek R    schedule 19.10.2020


Ответы (3)


Есть ли способ исправить is_hello_2 и is_hello_4?

Удалите constexpr из ifs в is_hello_2 или is_hello_4.

Как сравнить string_view с помощью if-constexpt в контексте constexpr

Нормально, как и везде.

static constexpr bool is_hello_5() {
    constexpr const std::string_view s1 ("hello");
    if constexpr (s1 == "hello") {
        return true;
    }
    return false;
}

Значения аргументов функции не являются постоянными выражениями, и вы не можете использовать их в if constexpr.

person KamilCuk    schedule 19.10.2020

Я не буду использовать формальную стандартную формулировку, а скорее объясню проблему.

if constexpr требует, чтобы все его аргументы всегда были constexpr.

Аргументы для constexpr функций иногда constexpr.

Вы можете вызывать constexpr функции с аргументами, отличными от constexpr.

Попробуйте consteval.

#include <string_view>

using namespace std::literals;

consteval bool is_hello_1(auto s) {
  return s == "hello";
}

consteval bool is_hello_2(auto s) {
    if (s == "hello") {
        return true;
    }
    return false;
}


int main(int argc, char **argv) {

    static constexpr std::string_view s1 ("hello");
    static_assert(s1 == "hello");
    static_assert(is_hello_1(s1));
    static_assert(is_hello_2("hello"sv));
    return 0;
}

Живой пример

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

Наконец, я бы посоветовал использовать анонимные пространства имен для static функций, локальных для файлов C ++, и не использовать ни то, ни другое в заголовках.


Это может не сделать все, что вы хотите; Преимущество if constexpr в том, что вы можете выполнять действия с неверным типом на основе ветки. А C ++ не позволяет делать вещи с недопустимым типом на основе значения аргумента функции; в противном случае компилятор не смог бы скомпилировать тело функции без предоставления значений ее аргументов.

Чтобы обойти это, вы можете сделать что-то вроде создания строки времени компиляции.

template<auto s>
consteval bool is_hello_2() {
  if constexpr (s == "hello") {
    return true;
  }
  return false;
}

и назови это с

template<std::size_t N>
struct compile_time_string : std::array<char, N+1> {
    constexpr std::array<char, N+1>& buffer() { return *this; }
    constexpr std::array<char, N+1> const& buffer() const { return *this; }
    constexpr std::string_view view() const { return {this->data(), this->data()+this->size()}; }
    
private:
    template<std::size_t...Is>
    constexpr compile_time_string( char const* str, std::index_sequence<Is...> ):
        std::array<char, N+1>{{ str[Is]..., char(0) }}
    {}
public:
    explicit constexpr compile_time_string( char const* str ):
        compile_time_string( str, std::make_index_sequence<N>{} )
    {}
    explicit constexpr compile_time_string( std::array<char, N+1> buff ) :
        std::array<char, N+1>(buff)
    {}

    constexpr compile_time_string( compile_time_string const& ) = default;
    compile_time_string() = delete;
    
        
    constexpr auto operator<=>( compile_time_string const& o ) const = default;
    constexpr bool operator==( compile_time_string const& o ) const = default;
    
    template<std::size_t N_arg>
    friend constexpr auto operator==( char const(&lhs)[N_arg], compile_time_string const& rhs )
    {
        return std::string_view{ lhs, lhs+N_arg } == rhs.view();
    }
    template<std::size_t N_arg>
    friend constexpr auto operator==( compile_time_string const& lhs, char const(&rhs)[N_arg] )
    {
        return lhs.view() == std::string_view{ rhs, rhs+N_arg };
    }
};
template<std::size_t N>
compile_time_string( char const(&)[N] )->compile_time_string<N-1>;


template<auto s>
consteval bool is_hello_3() {
    if (s == "hello") {
        return true;
    }
    return false;
}


static_assert(is_hello_3<compile_time_string("hello")>());

Живой пример.

Я урезал строку времени компиляции. Вы захотите лучше ‹=› и == и тому подобное для большего количества типов.

Кроме того, я думаю, что в C ++ 20 вы можете сделать более сильные строки времени компиляции, где строка фактически живет в аргументе auto. Но я не уверен.

person Yakk - Adam Nevraumont    schedule 19.10.2020

Подумайте с точки зрения функции (как если бы вы находились внутри области действия функции): как он может проанализировать ваш код во время компиляции, если аргументы функции неизвестны? По этой же причине вы помечаете s1 как constexpr в основной функции. Если этого не сделать - код не скомпилируется.

Итак, что вам нужно сделать, это удалить constexpr из is_hello2 и is_hello4. Это не мешает использовать функцию в constexpr.

#include <string_view>

static constexpr bool is_hello_1(auto s) {
  return s == "hello";
}

static constexpr bool is_hello_2(auto s) {
    if (s == "hello") {
        return true;
    }
    return false;
}

static constexpr auto is_hello_3 = [](auto s) {
    return s == "hello";
};

static constexpr auto is_hello_4 = [](auto s) {
    if (s == "hello") {
        return true;
    }
    return false;
};

int main(int argc, char **argv) {

    static constexpr std::string_view s1 ("hello");
    static_assert(s1 == "hello");
    static_assert(is_hello_1(s1));
    static_assert(is_hello_2("hello"));
    static_assert(is_hello_3(s1));
    static_assert(is_hello_4(s1));
    return 0;
}

Но вполне нормально использовать if constexpr для параметров шаблона типа и без типа:

template<int s>
static constexpr auto is_5 = []() {
    if constexpr (s == 5) {
        return true;
    }
    return false;
};

static_assert(is_5<5>());

К сожалению, на данный момент у вас не может быть std :: string_view в качестве параметра шаблона, не являющегося типом.

person Vasilij    schedule 19.10.2020