Ivaylo Valchev
Ivaylo Valchev

Reputation: 10425

Non-type template argument narrowing in template recursion

I have the following code, which is supposed to calculate the number of bits in a byte at compile time.

template<unsigned char c, size_t I>
struct char_bit
{
    static constexpr size_t get() noexcept {
        return c > 0 ? char_bit<c << 1, I + 1>::get() : I
    }
};

int main()
{   
    std::cout << char_bit<1, 0>::get();
}

By passing in 1 to the unsigned char parameter, I'm expecting to get a final result of 8 as it will shift left 8 times until the char becomes 0.

However, compiling with Clang 3.7.1, I get a compilation error:

error: non-type template argument evaluates to 256, which cannot be narrowed to type 'unsigned char' [-Wc++11-narrowing]

Why does this happen? How can I fix it?

Upvotes: 4

Views: 386

Answers (4)

Smeeheey
Smeeheey

Reputation: 10336

One way to avoid this error is to go in reverse: instead of overflowing your char, you start with the maximum and right-shift until you hit zero. You will need a specialisation to make the code work (you will have needed it in either case anyway):

#include <iostream>
using namespace std;

template<unsigned char c, size_t I>
struct char_bit
{
    static constexpr size_t get() noexcept {
        return char_bit< (c >> 1), I + 1>::get();
    }
};

template<size_t I>
struct char_bit<0, I>
{
    static constexpr size_t get() noexcept {
        return I;
    }
};


int main()
{   
    constexpr unsigned char c = static_cast<unsigned char>(-1);
    std::cout << char_bit<c, 0>::get();
}

Upvotes: 4

majk
majk

Reputation: 857

There is already a standard macro for that:

#include <climits>

int main() {
    std:cout << CHAR_BIT << std::endl;
}

Upvotes: 2

Sven Nilsson
Sven Nilsson

Reputation: 1869

OP said this works, so I'm posting it as answer:

char_bit<(unsigned char)(c << 1), I + 1>::get()

Upvotes: 1

I am not sure why it happens. But I think, you can fix it using template function specialization.

template<unsigned char c, size_t I>
struct char_bit
{
    static constexpr size_t get() noexcept {
        return char_bit<(c << 1), I + 1>::get();
    }
};

template <size_t I>
struct char_bit<0, I>
{
    static constexpr size_t get() noexcept { return I; }
};

Upvotes: 0

Related Questions