If you look at standard algorithms like std::ranges::fill
and std::ranges::generate
, they both seem to use additional parameters to deduce the range value type for their output ranges. E.g. ranges::fill(v, 10)
is able to deduce the value type T
because of its second argument.
However, when you try to define a similar function but take the second argument away, C++ is no longer able to deduce the value type. For instance, consider the following function:
template<typename T, std::output_range<const T&> R>
void ones(R && r)
{
for (T & value : r) {
value = 1;
}
}
The code will not compile if you try to call it without explicitly specifying the value type:
std::array<int, 3> a;
// ones(a); // does not compile
ones<int>(a); // redundant!
When I try to compile the commented-out code, I get the following error:
/home/josiest/sandbox/cpp/template-template-range/sketch.cpp: In function ‘int main()’:
/home/josiest/sandbox/cpp/template-template-range/sketch.cpp:19:9: error: no matching function for call to ‘ones(std::array<int, 3>&)’
19 | ones(a);
| ~~~~^~~
/home/josiest/sandbox/cpp/template-template-range/sketch.cpp:9:6: note: candidate: ‘template<class T, class R> requires output_range<R, T> void ones(R&&)’
9 | void ones(R && r)
| ^~~~
/home/josiest/sandbox/cpp/template-template-range/sketch.cpp:9:6: note: template argument deduction/substitution failed:
/home/josiest/sandbox/cpp/template-template-range/sketch.cpp:19:9: note: couldn’t deduce template parameter ‘T’
19 | ones(a);
| ~~~~^~~
Is there some way to deduce the range value type that I'm missing, or is it just not possible?
It sounds like you want:
template <typename T> requires std::output_range<T, std::ranges::range_value_t<T>>
Or, perhaps:
template <typename T> requires std::output_range<T, const std::ranges::range_value_t<T> &>
The former expects the element to be moved, and latter expects it to be copied. If you want to support both, you can use both constraints.