2

I tried to write a function, which returns an integer with only the highest bit of the input set, using a C++0x constexpr.

constexpr inline uint64_t
get_highest_bit(uint64_t p)
{
  return 
     (p|=(p>>1)),
     (p|=(p>>2)),
     (p|=(p>>4)),
     (p|=(p>>8)),
     (p|=(p>>16)),
     (p|=(p>>32)),
     (p-(p>>1));
}

This gives a compile-time failure using gcc 4.6.1.

error: expression ‘(p <unknown operator> ((p >> 1) | p))’ is not a constant-expression

Note that it works without the constexpr keyword.

My questions are:

Why does this not work? I can see that operator|= is not a constexpr, but does it matter for built-in types?

Is there an easy way to write this function as a constexpr? I would like it to be reasonable efficient at runtime, and I care a bit about readibility.

3
  • 2
    You cannot perform assignment in a constant expression and you cannot use the comma operator (I'm not 100% sure about the comma operator; you certainly couldn't use it in C++03). All you have to do is rewrite this so it doesn't use those two operators. It should be straightforward using recursion. Commented Jul 23, 2011 at 15:25
  • Thanks fot the quick answer. the comma operator seems to be accepted by g++ 4.6.1 at least. I was not aware of forbidden assignment, and at first sight, it seems to make constexpr a lot less neat. Commented Jul 23, 2011 at 15:39
  • @DirkM: constexpr seems really neat, until you learn about all the restrictions. Commented Jul 23, 2011 at 15:41

2 Answers 2

8

(Not tested on GCC because I don't have 4.6, but I've verified that the algorithm is correct.)

To use constexpr you must not have assignments. Therefore, you often have to write in functional form with recursion:

#include <cstdint>
#include <climits>

constexpr inline uint64_t highestBit(uint64_t p, int n = 1) {
    return n < sizeof(p)*CHAR_BIT ? highestBit(p | p >> n, n * 2) : p - (p >> 1);
}

int main() {
    static_assert(highestBit(7) == 4);
    static_assert(highestBit(5) == 4);
    static_assert(highestBit(0x381283) == 0x200000);
    return 0;
}

You can check C++0x §[expr.const]/2 to see what expressions cannot be used in a constexpr function. In particular, the second to last item is "an assignment or a compound assignment".

Sign up to request clarification or add additional context in comments.

3 Comments

That was insightful. I was hoping to get away from recursive tmeplate instantiations. It seems that I will have to learn to live with recursive constexpressions.
@DrikM: There are no templates involved, it's just plain recursion.
@Vitus: the original code (not published here) used templates to do the same as the constexpr here. (My comment was not very clear without context).
3
constexpr inline uint64_t highestBit(uint64_t p)
{
    return (p & (p-1))? highestBit(p & (p-1)): p;
}

Each level of recursion clears the rightmost bit that was set, when the final bit would be cleared, only the highest bit remained, so it's returned.

2 Comments

Thanks, I picked the other answer because the worst-case recursion is less deep there, and the compiler could theoretically unroll it. (although, I will have to look closer at my usecase).
@DirkM: The compiler should unroll either one at compile time. But when the argument isn't a constant, yes this one could involve more recursion (although, the compiler may still transform it to iteration with no additional function calls).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.