Reputation: 427
I have the following code:
constexpr uint32_t countWords(const char* str) {
constexpr std::size_t length = std::char_traits<char>::length(str);
std::uint32_t count = 0;
for (std::size_t i = 0; i < length; i++) {
if (str[i] == ' ') {
count++;
}
}
return count;
}
My problem arises on the first line of the function where I get a syntax error stating that:
str cannot be used as a constant
when I try to pass it to std::char_traits<char>::length
. If I remove the constexpr from the length
variable the error goes away, but to me that implies that the variable is not obtainable at compile time which defeats the purpose of the constexpr function. I plant call this functions using string literals as the parameter.
Upvotes: 2
Views: 698
Reputation: 4490
Another solution is to avoid std::char_traits::length and use old, good template that will give you array size at compile time:
constexpr const char words[] = "SOME WORDS AND MORE";
template <size_t length>
constexpr size_t countWords(const char (&str)[length]) {
size_t count = 1;
for (std::size_t i = 0; i < length; i++) {
if (str[i] == ' ') {
count++;
}
}
return count;
}
int main() {
constexpr auto words = countWords(words);
printf("%d: ", words); // 4
}
ps. Note about difference between array size and string size. Array of chars will include zero terminator. pss. Actually it's more like countSpaces function :)
Upvotes: 0
Reputation: 12263
First, you need to make your compiler know that the length will be calculated at compile-time. With your current implementation, str
parameter could be passed to the function call at both compile time and runtime (if you didn't know, constexpr
is not forced to be compile time, it can be executed at runtime too; check for consteval
from C++20 which forces compile time calculation).
So, in order to make sure your str
variable is passed at compile time, you may want to pass it as non-type template parameter like:
template <char const * S>
constexpr uint32_t countWords() {
constexpr std::size_t length = std::char_traits<char>::length(S);
std::uint32_t count = 0;
for (std::size_t i = 0; i < length; i++) {
if (S[i] == ' ') {
count++;
}
}
return count;
}
but, please note, that this will work only if your S
pointer has static storage, so following would work:
static constexpr char str[]{ "a b c" };
constexpr auto cnt = countWords<str>();
but following would NOT work:
constexpr char str[]{ "a b c" };
constexpr auto cnt = countWords<str>(); // ERROR
For more info, please refer to this question here.
Apart of this, your countWords
function does not do the right thing because the above example will set variable cnt
to value 2 which is not right.
EDIT:
If you want to use function on string literals, then the other answer describes the fix.
Upvotes: 1
Reputation: 12928
From the comments it seems you are interested in using this on string literals.
All you need to do to make that work is to remove constexpr
from length
.
The function has to be callable at run-time as well as compile-time.
But when you call it with a string literal it can be calculated at compile time. You can verify this by assigning the return value of the function to a constexpr
variable.
#include <iostream>
#include <string>
constexpr uint32_t countWords(const char* str) {
std::size_t length = std::char_traits<char>::length(str);
std::uint32_t count = 0;
for (std::size_t i = 0; i < length; i++) {
if (str[i] == ' ') {
count++;
}
}
return count;
}
int main()
{
constexpr auto wordcount = countWords("This is a sentence");
std::cout << wordcount;
}
Upvotes: 3