7

Currently only MSVC supports the nifty helper ranges::to, so I am unable to validate this in another compiler. Basically I have a type alias for a STL container and as soon as I attempt to pass it to ranges::to, compilation fails. So is this a valid usage? Why does the second (commented-out) example below not compile?

#include <ranges>
#include <vector>
#include <iostream>

template <typename T>
using Vec = std::vector<T>;

int main(int argc, char* argv[]) {
    auto vec = std::views::iota(1, 10) | std::ranges::to<std::vector>();
    //auto vec = std::views::iota(1, 10) | std::ranges::to<Vec>(); // C2440: cannot convert from 'void' to 'std::vector'.

    for (auto& v : vec)
        std::cout << v << ", ";

    std::cout << std::endl;
}
Carsten
  • 11,287
  • 7
  • 39
  • 62
  • For what it is worth, I used [range-v3](https://ericniebler.github.io/range-v3/) and C++17 and (after changing to the range namespace) both vector productions worked for me using clang++ on Unix. I do recommend giving range-v3 a try with MSVC. The range-v3 is the origin of ranges support in C++20 . – Eljay Apr 19 '23 at 12:33
  • 2
    `std::vector` has more than 1 template parameter. The alias `Vec` should be `template using Vec = std::vector;` and that should work. – cigien Apr 19 '23 at 12:37
  • @cigien Yes, that works. If your only problem was that you don't have access to a current MSVC compiler, then trust my word and post the answer. :) – Adrian Mole Apr 19 '23 at 12:41
  • @cigien: Right, that works, thanks! But I'm not quite sure why omitting it does not work, since the second type is defaulted to `std::allocator`. So I suspected this should also work: `using Vec = std::vector>;` and also tried this earlier. However, it doesn't. Does `ranges::to` need to pass the allocator? – Carsten Apr 19 '23 at 12:51

1 Answers1

3

Here's a reduced example, dropping ranges stuff

#include <type_traits>

template <typename T, typename Alloc=T>
struct A {
    template <typename R>
    A(R&&, Alloc={});
};

template <typename R, typename Alloc=std::type_identity_t<R>>
A(R&&, Alloc={}) -> A<std::type_identity_t<R>, Alloc>;

#if 0
    template <typename T> using B = A<T>;
#elif 0
    template <typename T, typename U=T> using B = A<T, U>;
#elif 0
    template <typename... Ts> using B = A<Ts...>;
#endif

int main(int argc, char* argv[]) {
    auto a = A(1);
    auto b = B(1);
}

There are three different approaches to implementing B, which should probably behave the same for this example, but don't:

  • everybody rejects the unary alias
  • gcc accepts the binary alias, but only if U is defaulted
  • gcc and msvc accept the variadic alias

I don't really know who's right, the rules for class template argument deduction for alias ([over.match.class.deduct], starting at /3) are pretty involved. It sure seems like the three definitions should give you the same result here, but...


Either way, it is definitely valid to use alias templates with ranges::to. It's just a matter of what the actual rules for deduction are from alias templates and to what extent they actually work.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks, this answers my question, although it's a little bit unsatisfactory. :-D I guess I stick to `ranges::to>` then, since this deducts the actual type. :-) – Carsten Apr 19 '23 at 13:42