Suppose I'd like to write a function that accepts anything I can iterate over with a size of exactly 2
. How might I write a concept to do that? The closest I think I've gotten is the following:
#include <array>
#include <concepts>
#include <iostream>
#include <ranges>
template<typename T, size_t N>
concept RangeOfSize =
std::ranges::sized_range<T> &&
requires (T i) { i.size() == N; }
;
void f(RangeOfSize<2> auto&& r) { std::cout << r.size() << std::endl; }
int main() {
f(std::array<uint8_t, 2>{2, 3});
f(std::array<uint8_t, 3>{5, 7, 11});
}
g++ -std=c++23 $0 -o exe -Wall -Werror -Wextra -pedantic-errors
Which compiles fine, much to my chagrin, and outputs
2
3
Ideally it would fail to compile on the second call to f
because that array is not 2 elements long. HIGHLY ideally it would fail with a nice-to-read error but I'm giving up hope on that.
(Bonus points for an explanation on how I might have figured out the answer myself).
You can just use a static_assert
to accomplish this:
void f(auto&& r)
{
static_assert(r.size() == 2, "Only ranges of size 2 supported");
std::cout << r.size() << std::endl;
}
For
f(std::array<int, 3>{5, 7, 11});
GCC gives the follwing output:
<source>: In instantiation of 'void f(auto:10&&) [with auto:10 = std::array<int, 3>]':
<source>:18:6: required from here
18 | f(std::array<int, 3>{5, 7, 11});
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:12:28: error: static assertion failed: Only ranges of size 2 supported
12 | static_assert(r.size() == 2, "Only ranges of size 2 supported");
| ~~~~~~~~~^~~~
<source>:12:28: note: the comparison reduces to '(3 == 2)'
Compiler returned: 1