3

I have a function which looks like this:

template <typename T, std::size_t... I>
std::ostream& vector_insert(std::ostream& lhs, const char* delim, const T& rhs, std::index_sequence<I...>) {
    std::ostream_iterator<float> it(lhs, delim);

    ((*it++ = at(rhs, I)), ...);
    return lhs;
}

This is my final attempt and I'm still failing on my expansion of the integer_sequence I'm hoping someone can tell me how to write a line that will effectively expand to:

*it++ = at(rhs, 0U), *it++ = at(rhs, 1U), *it++ = at(rhs, 2U)

Other things I've tried are:

  1. *it++ = at(rhs, I...)
  2. *it++ = at(rhs, I)...
  3. (*it++ = at(rhs, I))...

All of them are giving me the error:

error C3520: I: parameter pack must be expanded in this context

How do I expand this thing?

EDIT:

@AndyG has pointed out that this seems to be a bug.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • What does `*it++ = ` even mean? Post-increment an assignment *and* a de-reference? Multiple post-increment operators on a single line is really not good. – tadman Jun 14 '19 at 18:17
  • 4
    Not the downvoter, but providing a [mcve] w/ an online compiler might help us off to a better start. – AndyG Jun 14 '19 at 18:17
  • 1
    @tadman `*it++` just evaluates to `it` in this case cause `*` and `++` are no-ops on an `ostream_iterator`. – Jonathan Mee Jun 14 '19 at 18:20
  • Why are you assigning to that? None of this code makes any sense. What are you trying to achieve? Can you show how this code is used, and what the output should be? – tadman Jun 14 '19 at 18:20
  • 3
    Your code [works fine](http://coliru.stacked-crooked.com/a/06d4ae8b717e5681) for me. – Miles Budnek Jun 14 '19 at 18:21
  • @AndyG I'll try to get a better MCVE in a few minutes, the code necessary for reproducing the error was pretty substantial in my first attempt. – Jonathan Mee Jun 14 '19 at 18:21
  • 1
    @tadman That's what an `ostream_iterator` is for. Pushing things to an `ostream` (like `std::cout`) as-if it was an range. – François Andrieux Jun 14 '19 at 18:21
  • 1
    Looks like a MSVC compiler's bug. Can't reproduce with gcc 8.3. – Evg Jun 14 '19 at 18:22
  • 1
    This looks like a VS thing. I've had problems in the past getting it to expand parameter packs for code that otherwise seems valid. Edit : Let me see if I can find the workaround I used. – François Andrieux Jun 14 '19 at 18:23
  • @MilesBudnek I'm using Visual Studio 2017... maybe that's my problem :/ – Jonathan Mee Jun 14 '19 at 18:23
  • Which version of VS exactly? – Evg Jun 14 '19 at 18:23
  • @tadman Typo on my part with the 3. I've edited. – Jonathan Mee Jun 14 '19 at 18:24
  • @JonathanMee: make sure you tell VS to enable C++17, it doesn't by default. Not sure if there will still be a bug, but worth a shot – AndyG Jun 14 '19 at 18:24
  • @Evg Visual Studio 2017 version 15.6.7 – Jonathan Mee Jun 14 '19 at 18:25
  • 1
    @JonathanMee From what I see, the workaround is pretty much to reimplement the logic in a recursive form. Then, the only expansion is of `I` as a template argument for the next recursion iteration. – François Andrieux Jun 14 '19 at 18:29
  • 1
    Similar bug: https://developercommunity.visualstudio.com/content/problem/250767/compile-error-while-splitting-template-class-metho.html – Evg Jun 14 '19 at 18:30
  • 1
    I'm able to reproduce with MSVC 19.00, but it works with MSVC 19.14. Looks like you just need to update Visual Studio. – Miles Budnek Jun 14 '19 at 18:40

1 Answers1

2

This seems like a compiler bug with Visual C++. I'm not aware of any easy fix for it other than simplifying the expression in which the parameter pack is expanded. Converting to a recursive approach seems to reliably work around the problem. For example :

#include <array>
#include <iostream>
#include <iterator>

template <typename T>
const auto& at(const T& v, size_t i) { return v[i]; }

// End of recursion
template<class T>
void vector_insert_impl(std::ostream_iterator<int> &, const char*, const T&)
{}

// Recursion case
template<class T, std::size_t N, std::size_t... I>
void vector_insert_impl(std::ostream_iterator<int> & iter, const char* delim, const T&rhs)
{
    *iter++ = at(rhs, N);

    // Continue the recursion
    vector_insert_impl<T, I...>(iter, delim, rhs);
}

template <typename T, std::size_t... I>
std::ostream& vector_insert(std::ostream& lhs, const char* delim, const T& rhs, std::index_sequence<I...>) 
{
    std::ostream_iterator<int> it(lhs, delim);

    // Call the recursive implementation instead
    vector_insert_impl<T, I...>(it, delim, rhs);

    return lhs;
}

int main() {
    std::array<int, 5> v = { 1, 2, 3, 4, 5 };
    vector_insert(std::cout, " ", v, std::make_index_sequence<v.size()>());
}

Here, the parameter pack I is only expanded in the context of providing template parameters which VC++ has no trouble with.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • I've gone ahead and accepted it, cause it works perfectly for my toy example. I've tried to build on this answer a bit though here: https://stackoverflow.com/q/56653029/2642059 and I'm running into trouble. Perhaps you can provide some insight? – Jonathan Mee Jun 18 '19 at 16:02