palacaze / sigslot

A simple C++14 signal-slots implementation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Perfect forwarding of slots only possible with auto&& parameter pack

djromberg opened this issue · comments

First of all: Great library, I completely switched from Boost.Signals2 to your implementation.
This could be the wrong place, however, I have problems with defining perfect forwarding slots. Given these two connection examples to a print function taking a universal reference:

#include <iostream>
#include <sigslot/signal.hpp>

void print(int&& i)
{
  std::cout << std::forward<int>(i) << std::endl;
}

int main()
{
  sigslot::signal<int> sig;

  // does not compile: cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’
  sig.connect([](int&& i) {
    print(std::forward<int>(i));
  });

  // compiles and works
  sig.connect([](auto&& ...i) {
    print(std::forward<int>(i)...);
  });
  
  sig(42);
  return 0;
}

Only the second one compiles, the other one gives me an error: cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’
This might be related to my compiler (gcc 8.2.1), but maybe you have a simple explanation.

Best regards
Daniel

Ok, I think I learned why this happens: It is not the variadic parameter pack, but the "auto" that will make the lambda a templated one. A template parameter T&& will match "more" due to the reference collapsing rules. Unlike int&&, T&& will collapse to either T& or T&& depending on how the function was called.
I now use this for my perfect forwarding slot:

sig.connect([&target](auto&& value) {
  target.push(std::forward<decltype(value)>(value));
});

Sorry for the noise!

I am glad you find the library useful.

Yes, your $int&amp;&amp;$ parameter is not a forwarding reference (official name universal reference) but an rvalue parameter, due to the lack of some sort of templating.