Read it backwards (as driven by Clockwise/Spiral Rule):
int*
- pointer to int
int const *
- pointer to const int
int * const
- const pointer to int
int const * const
- const pointer to const int
Now the first const
can be on either side of the type so:
const int *
== int const *
const int * const
== int const * const
If you want to go really crazy you can do things like this:
int **
- pointer to pointer to int
int ** const
- a const pointer to a pointer to an int
int * const *
- a pointer to a const pointer to an int
int const **
- a pointer to a pointer to a const int
int * const * const
- a const pointer to a const pointer to an int
- ...
If you're ever uncertain, you can use a tool like cdecl+ to convert declarations to prose automatically.
To make sure we are clear on the meaning of const
:
int a = 5, b = 10, c = 15;
const int* foo; // pointer to constant int.
foo = &a; // assignment to where foo points to.
/* dummy statement*/
*foo = 6; // the value of a canĀ“t get changed through the pointer.
foo = &b; // the pointer foo can be changed.
int *const bar = &c; // constant pointer to int
// note, you actually need to set the pointer
// here because you can't change it later ;)
*bar = 16; // the value of c can be changed through the pointer.
/* dummy statement*/
bar = &a; // not possible because bar is a constant pointer.
foo
is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to a const char
. You may change which string you point to but you can't change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn't be changed.
bar
is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use a T* const
pointer unless you need to allow NULL
pointers.
Setting a bit
Use the bitwise OR operator (|
) to set n
th bit of number
to 1
.
// Can be whatever unsigned integer type you want, but
// it's important to use the same type everywhere to avoid
// performance issues caused by mixing integer types.
typedef unsigned long Uint;
// In C++, this can be template.
// In C11, you can make it generic with _Generic, or with macros prior to C11.
inline Uint bit_set(Uint number, Uint n) {
return number | ((Uint)1 << n);
}
Note that it's undefined behavior to shift by more than the width of a Uint
. The same applies to all remaining examples.
Clearing a bit
Use the bitwise AND operator (&
) to set the n
th bit of number
to 0
.
inline Uint bit_clear(Uint number, Uint n) {
return number & ~((Uint)1 << n);
}
You must invert the bit string with the bitwise NOT operator (~
), then AND it.
Toggling a bit
Use the bitwise XOR operator (^
) to toggle the n
th bit of number
.
inline Uint bit_toggle(Uint number, Uint n) {
return number ^ ((Uint)1 << n);
}
Checking a bit
You didn't ask for this, but I might as well add it.
To check a bit, shift number
n
to the right, then bitwise AND it:
// bool requires #include <stdbool.h> prior to C23
inline bool bit_check(Uint number, Uint n) {
return (number >> n) & (Uint)1;
}
Changing the nth bit to x
There are alternatives with worse codegen, but the best way is to clear the bit like in bit_clear
, then set the bit to value, similar to bit_set
.
inline Uint bit_set_to(Uint number, Uint n, bool x) {
return (number & ~((Uint)1 << n)) | ((Uint)x << n);
}
All solutions have been tested to provide optimal codegen with GCC and clang. See https://godbolt.org/z/Wfzh8xsjW.
Best Answer
The issue is that the
value_type
of a range is neverconst
-qualified. Butreference
type of the range could be - although there that's still wrong because it'd bestring const&
.Contiguous iterators are required to have their reference type to be
U&
for some typeU
, so if we simply drop the trailing reference we'll get a potentially const-qualified type:Note that you don't need to pass
r.begin()
andr.end()
intospan
,span
has a range constructor.However, this still isn't quite correct for a reason that the original was also wrong.
contiguous
is an insufficient criteria here, we also need the range to besized
so that we can construct aspan
out of it:Also you probably don't want to deduce
T
from the argument - you really want it to be specifically the correctT
associated with the range:Note that I changed your code to construct the
span
using parentheses instead of braces. This is because passing an iterator pair into an initializer using braces is not a good idea, because it could easily do the wrong thing. Consider:And the same is true for
span
, which will have aninitializer_list
constructor starting in C++26.