Given the following sample program, which makes use of the std::uniform_random_bit_generator
concept:
#include<random>
#include<print>
template<std::uniform_random_bit_generator PRNG>
void test(PRNG && rng) {
std::uniform_int_distribution<int> dist{1, 7};
std::print("{}\n", dist(rng));
}
int main() {
std::minstd_rand engine{std::random_device{}()};
test(engine);
}
I expect this code to compile, but it fails with the following error messages.
What confuses me is that I haven't defined any new types: this uses a built in C++ random bit generator, and I get the same error when using other generators (mt19937, etc.). Why is this happening? Am I applying the concept incorrectly?
<source>:12:5: error: no matching function for call to 'test'
12 | test(engine);
| ^~~~
<source>:5:6: note: candidate template ignored: constraints not satisfied [with PRNG = std::minstd_rand &]
5 | void test(PRNG && rng) {
| ^
<source>:4:10: note: because 'std::linear_congruential_engine<unsigned long, 48271, 0, 2147483647> &' does not satisfy 'uniform_random_bit_generator'
4 | template<std::uniform_random_bit_generator PRNG>
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/uniform_int_dist.h:57:4: note: because '_Gen::min()' would be invalid: type 'std::linear_congruential_engine<unsigned long, 48271, 0, 2147483647> &' cannot be used prior to '::' because it has no members
57 | { _Gen::min() } -> same_as<invoke_result_t<_Gen&>>;
| ^
1 error generated.
Compiler returned: 1
<source>: In function 'int main()':
<source>:12:9: error: no matching function for call to 'test(std::minstd_rand&)'
12 | test(engine);
| ~~~~^~~~~~~~
<source>:5:6: note: candidate: 'template<class PRNG> requires uniform_random_bit_generator<PRNG> void test(PRNG&&)'
5 | void test(PRNG && rng) {
| ^~~~
<source>:5:6: note: template argument deduction/substitution failed:
<source>:5:6: note: constraints not satisfied
In file included from /opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/random.h:35,
from /opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/random:48,
from <source>:1:
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/uniform_int_dist.h: In substitution of 'template<class PRNG> requires uniform_random_bit_generator<PRNG> void test(PRNG&&) [with PRNG = std::linear_congruential_engine<long unsigned int, 48271, 0, 2147483647>&]':
<source>:12:9: required from here
12 | test(engine);
| ~~~~^~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/uniform_int_dist.h:53:13: required for the satisfaction of 'uniform_random_bit_generator<PRNG>' [with PRNG = std::linear_congruential_engine<long unsigned int, 48271, 0, 2147483647>&]
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/uniform_int_dist.h:55:10: in requirements [with _Gen = std::linear_congruential_engine<long unsigned int, 48271, 0, 2147483647>&]
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/uniform_int_dist.h:57:20: note: the required expression '_Gen::min()' is invalid
57 | { _Gen::min() } -> same_as<invoke_result_t<_Gen&>>;
| ~~~~~~~~~^~
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/uniform_int_dist.h:58:20: note: the required expression '_Gen::max()' is invalid
58 | { _Gen::max() } -> same_as<invoke_result_t<_Gen&>>;
| ~~~~~~~~~^~
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/uniform_int_dist.h:59:18: note: nested requirement 'std::bool_constant<((bool)(_Gen::min() < _Gen::max()))>::value' is not satisfied
59 | requires bool_constant<(_Gen::min() < _Gen::max())>::value;
| ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
Compiler returned: 1
This is a tricky and a bit confusing C++ part even for me. So after a check on the <random>
documentation and C++20 Concepts
, here is why:
The uniform_random_bit_generator
concept is failing because of two related issues:
We have to either pass PRNG directly instead of passing it with a forwarding reference or work around the Concept Checks.
For the second solution, we would use this: std::remove_reference_t
So it would be something like this:
template<typename PRNG>
requires std::uniform_random_bit_generator<std::remove_reference_t<PRNG>>
void test(PRNG && rng)
Hope this was a helpful answer. Concepts are still a challenging topic for me in C++.