3

I was able to find one question on SO that seems to be asking the same or similar question that I am, but there are no answers :-(

I'd like to place a non-template parameter after a parameter pack. I'm not too familiar on the C++ standard specification for variadic templates / parameter packs, but my common sense assumption tells me that the right-most parameters passed to a function would be filled into placement parameters first, then the rest get filled into the parameter pack. However, I'm not able to get my test code working on either g++ or clang++. Sample code below.

#include <vector>
#include <iostream>

int Subscribe(int channel, int callback)
{
    return channel;
}

// This one works fine...
template<typename... T>
std::vector<int> SubscribeMultiple1(int callback, T&&... channels)
{
    return {
        Subscribe(std::forward<T>(channels), std::move(callback))...
    };
}

// This one does not work; all I did was move `int callback` after the parameter pack.
template<typename... T>
std::vector<int> SubscribeMultiple2(T&&... channels, int callback)
{
    return {
        Subscribe(std::forward<T>(channels), std::move(callback))...
    };
}

int main()
{
    auto subs = SubscribeMultiple2(1, 2, 3);

    for (auto sub : subs)
    {
        std::cout << "Sub: " << sub << '\n';
    }
}

Live Sample

So my first question is, why does the non-template parameter not work after the parameter pack? Am I doing something wrong or is this prohibited by the language?

Secondly, is there any way to get the syntax I'm trying to do? I've oversimplified my sample, but in my real code the callback parameter is really a std::function<...>. The idea is I can subscribe to a number of "event IDs", and the callback is defined at the end. Having the callback at the end is better for readability and style. Example:

SubscribeMultiple(EventOne, EventTwo, EventThree, [] {
    // Implement callback code here when any of the above
    // three events are fired.
});

If I have to have the callback in the front, it's less readable IMHO. So I'm willing to try any workaround to get the syntax and structure I'm aiming for. Thanks in advance.

Community
  • 1
  • 1
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • I think the compiler cannot determine where the parameter pack ends due to the way in which it evaluates. If I remember rightly, this guy explains it well: https://youtu.be/VIz6xBvwYd8?list=PLHTh1InhhwT7J5jl4vAhO1WvGHUUFgUQH&t=2309 – keith Feb 03 '17 at 14:54
  • Thanks @keith. Any text-version of that? – void.pointer Feb 03 '17 at 14:55
  • The slides for cppcon 2016 are all up on an official github page https://github.com/CppCon/CppCon2016. There is a 2016 talk just on variadics - also worth a look. – keith Feb 03 '17 at 14:58
  • P.S. if you want the variadics first, wrap 'em in a class! Then you can at least do SubscribeMultiple2({blah, blah,...}, callback) for just an extra two braces. – keith Feb 03 '17 at 15:00
  • Another simple workaround is to specify the types in the parameter pack explicitly: `SubscribeMultiple2(1, 2, 3)`, see https://godbolt.org/z/Kdxsjv4GP – blue Aug 03 '22 at 22:53

1 Answers1

7

Am I doing something wrong or is this prohibited by the language?

Deduction of parameter packs occurs only when the pack is the last argument.

[temp.deduct.type]/5.7:

The non-deduced contexts are:

  • [...]
  • A function parameter pack that does not occur at the end of the parameter-declaration-list.

So that's normal behavior of standard C++.

is there any way to get the syntax I'm trying to do?

Use the first syntax as a workaround:

   template<typename... T>
   std::vector<int> SubscribeMultiple1(int callback, T&&... channels)
rustyx
  • 80,671
  • 25
  • 200
  • 267