Reputation: 556
Is it possible to compare std::string_view using "if constexpr" in a constexpr context? And why is_hello_2 and is_hello_4 fail to compile showing error: "‘s’ is not a constant expression"
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;
};
Considering the main function (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 there a way to fix "is_hello_2" and "is_hello_4"?
Upvotes: 4
Views: 2554
Reputation: 275730
I won't use the formal standard wording, but rather explain the problem.
if constexpr
requires that all of its arguments are always constexpr
.
Arguments to constexpr
functions are sometimes constexpr
.
You can call constexpr
functions with non-constexpr
arguments.
Try 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;
}
You can put consteval
in a lambda where you'd usually put mutable (I didn't recall this off-hand, so I didn't include lambdas in my sample code above).
Finally, I'd advise using anonymous namespaces to static
functions local to C++ files, and use neither in headers.
This might not do everything you want; the advantage of if constexpr
is you can do type-invalid things based on the branch. And C++ doesn't let you do type-invalid things based on the value of a function argument; if it did, the compiler couldn't compile the body of a function without its argument values being supplied.
To get around that you can do something like create a compile-time string.
template<auto s>
consteval bool is_hello_2() {
if constexpr (s == "hello") {
return true;
}
return false;
}
and call it with
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")>());
I pared down the compile time string a bunch. You'll want better <=> and == and the like for more types.
Also, I think in C++20 you can make "stronger" compile time strings where the string actually lives in an auto argument. But I'm uncertain.
Upvotes: 3
Reputation: 1941
Think from the function perspective (as if you are inside the function scope): how can it parse your code in compile time if function arguments are unknown? That is the same reason you mark s1
as constexpr
in the main function. If you don't - the code won't compile.
So what you have to do is to remove constexpr
from is_hello2
and is_hello4
. This doesn't prevent the function from being used in 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;
}
But it is perfectly fine to use if constexpr on type and none-type template parameters:
template<int s>
static constexpr auto is_5 = []() {
if constexpr (s == 5) {
return true;
}
return false;
};
static_assert(is_5<5>());
Unfortunately you can't have std::string_view as a non-type template parameter for now.
Upvotes: 1
Reputation: 141473
Is there a way to fix "is_hello_2" and "is_hello_4"?
Remove the constexpr
from if
s in is_hello_2
or is_hello_4
.
How to compare string_view using if-constexpt in a constexpr context
Normally, like anywhere else.
static constexpr bool is_hello_5() {
constexpr const std::string_view s1 ("hello");
if constexpr (s1 == "hello") {
return true;
}
return false;
}
Function arguments values are not constant expressions and you can't use them in a if constexpr
.
Upvotes: 8