c++c++20type-deductionstd-ranges

Is it possible to deduce the value type of a range from the range argument?


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?


Solution

  • 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.