I am writing a wrapper around std::jthread
and some surrounding infrastructure. I cannot wrap my head around why the following won't compile:
#include <iostream>
#include <map>
#include <functional>
#include <thread>
// two random functions
void foo(int i) { std::cout << "foo " << i << std::endl; }
void bar(int i) { std::cout << "bar " << i << std::endl; }
// mechanism to identify them
enum function_kind {
foo_kind, bar_kind
};
std::map<function_kind, std::function<void(
int)>> kind_to_function{{foo_kind, foo},
{bar_kind, bar}};
// wrapper around jthread
// (additional functionality ommitted for brevity)
template<typename Callable, typename... Args>
class MyThread {
public:
explicit MyThread(Callable &&function, Args &&...args) : m_thread{
std::forward<Callable>(function),
std::forward<Args>(args)...} {}
private:
std::jthread m_thread;
};
int main() {
std::jthread t1(kind_to_function[foo_kind], 3); // works
MyThread t2(kind_to_function[foo_kind], 3); // complains
return 0;
}
I am really just trying to mimic whatever std::jthread
is doing with my own class.
The IDE (clion) complains, that the first argument to t2
is not an rvalue. The compiler complains a little more complicated:
main.cpp: In function ‘int main()’:
main.cpp:29:46: error: class template argument deduction failed:
29 | MyThread t2(kind_to_function[foo_kind], 3); // complains
| ^
main.cpp:29:46: error: no matching function for call to ‘MyThread(std::map<function_kind, std::function<void(int)> >::mapped_type&, int)’
main.cpp:20:14: note: candidate: ‘MyThread(Callable&&, Args&& ...)-> MyThread<Callable, Args> [with Callable = std::function<void(int)>; Args = {int}]’ (near match)
20 | explicit MyThread(Callable &&function, Args &&...args) : m_thread{std::forward<Callable>(function),
| ^~~~~~~~
main.cpp:20:14: note: conversion of argument 1 would be ill-formed:
main.cpp:29:46: error: cannot bind rvalue reference of type ‘std::function<void(int)>&&’ to lvalue of type ‘std::map<function_kind, std::function<void(int)> >::mapped_type’ {aka ‘std::function<void(int)>’}
29 | MyThread t2(kind_to_function[foo_kind], 3); // complains
| ^
main.cpp:18:7: note: candidate: ‘template<class Callable, class ... Args> MyThread(MyThread<Callable, Args>)-> MyThread<Callable, Args>’
18 | class MyThread {
| ^~~~~~~~
main.cpp:18:7: note: template argument deduction/substitution failed:
main.cpp:29:46: note: ‘std::function<void(int)>’ is not derived from ‘MyThread<Callable, Args>’
29 | MyThread t2(kind_to_function[foo_kind], 3); // complains
| ^
In any case, the arguments work for std::jthread
, which also just takes rvalues... So what am I missing?
CodePudding user response:
The parameters of the MyThread
constructor are not forwarding references because the constructor is not a template. Do not make the class a template, but only the constructor:
class MyThread {
public:
template<typename Callable, typename... Args>
explicit MyThread(Callable &&function, Args &&...args) :
m_thread{
std::forward<Callable>(function),
std::forward<Args>(args)...} {}
private:
std::jthread m_thread;
};
CodePudding user response:
In any case, the arguments work for
std::jthread
, which also just takes rvalues... So what am I missing?
jthread
is not a template, its constructor is a template. Which makes the rvalue references to template parameters into forwarding references, not plain rvalue references.
However, since MyThread
is itself a template, and its constructor is not a template constructor, the behavior is not the same. After instantiation, it's a regular constructor that accepts only rvalues.
Forwarding references are contingent on template argument deduction happening for the function template they are a part of. So a non-template constructor means no forwarding references.
Okay, but you didn't specify template arguments to MyThread
, why was there seemingly no error? Because class template argument deduction allows you to omit those. And CTAD happens in its own overload resolution step, completely disjoint from actually choosing a constructor to initialize the object. One step can be ill-formed while the other is not.