Ryoku
Ryoku

Reputation: 427

const char* cannot be used as a constant value for std::char_traits<char>::length

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.

std::char_traits::length

Upvotes: 2

Views: 698

Answers (3)

crea7or
crea7or

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

NutCracker
NutCracker

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

super
super

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

Related Questions