rvalue reference forwarding - c++

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?

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;
};

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.

Related

std::enable_if to conditionally disable a template constructor

I am trying to get a simple example to work to understand how to use std::enable_if, here is the problem:
I am reading the textbook C++ Templates The Complete Guide by David Vandevoorde, Nicolai M.Josuttis, Chapter 6, Section 5.
This chapter mentions: "std::enable_if to prevent being able to copy objects of a class template C<> if the template parameter is an integral type", and its following code:
template <typename T> class C {
public:
// user-define the predefined copy constructor as deleted (with conversion to
// volatile to enable better matches)
C(C const volatile &) = delete;
// if T is not integral type, provide copy constructor template with better match:
template <typename U,
typename = std::enable_if_t<!std::is_integral<U>::value>>
C(C<U> const &) {
std::cout << "tmpl copy constructor" << std::endl;
}
};
My question is, how should the above code be called and used?
for example, I tried:
C<int> c_int;
std::string s = "sname";
C<std::string> c_string1(std::string);
C<std::string> c_string2(c_string1);
But give me compile error:
specialmember3.cc:22:39: error: no matching function for call to ‘C<std::__cxx11::basic_string<char> >::C(C<std::__cxx11::basic_string<char> > (&)(std::string))’
22 | C<std::string> c_string2(c_string1);
| ^
specialmember3.cc:14:3: note: candidate: ‘template<class U, class> C<T>::C(const C<U>&)’
14 | C(C<U> const &) {
| ^
specialmember3.cc:14:3: note: template argument deduction/substitution failed:
specialmember3.cc:22:39: note: mismatched types ‘const C<U>’ and ‘C<std::__cxx11::basic_string<char> >(std::string)’ {aka ‘C<std::__cxx11::basic_string<char> >(std::__cxx11::basic_string<char>)’}
22 | C<std::string> c_string2(c_string1);
| ^
specialmember3.cc:9:3: note: candidate: ‘constexpr C<T>::C() [with T = std::__cxx11::basic_string<char>]’
9 | C() = default;
| ^
specialmember3.cc:9:3: note: candidate expects 0 arguments, 1 provided
Can someone please give me some hints or code guidance on how to use above template constructor?
You have declared c_string1 as a function. I think you meant this
C<std::string> c_string1;
C<std::string> c_string2(c_string1);
That class, as it is defined, is somewhat useless.
a) You can't default-initialize it, default constructor is removed. C<int> c_int; is ill-formed.
b) You can't create it from value, e.g. C<std::string> c_string1(some_str);, because that constructor does not exist.
Essentially you can copy it, but you cannot create it, which is a nonsense. It breaks rule of 3/5/0.
template <typename T>
class C {
public:
C() /*Initialization here */ {}
C(const T& val) /*Initialization here */ {
std::cout << "tmpl copy constructor: " << val << std::endl;
}
// user-define the predefined copy constructor as deleted (with conversion to
// volatile to enable better matches)
C(C const volatile &) = delete;
// if T is not integral type, provide copy constructor template with better match:
template <typename U,
typename = std::enable_if_t<!std::is_integral<U>::value>>
C(C<U> const &) {
std::cout << "tmpl copy constructor" << std::endl;
}
};
In that case those would be legal:
C<int> c_int;
std::string s = "sname";
C<std::string> c_string1(s);
C<std::string> c_string2(c_string1);
THe copy template defined disallows copying of C if T is integral, so
C<int> c_int2 (c_int); // use of deleted function 'C<T>::C(const volatile C<T>&)

lambda converted to bool instead of deducing function-pointer-type

I wanted to implement a overload for operator<< that allowed me to call a given function and output the result.
I therefore wrote an overload, but the conversion to bool is selected and when writing a function myself, it would not compile.
EDIT: Know that I do not want to call the lambda,
but instead pass it to the function where it should be called with a default constructed parameter list.
I have appended my code:
#include <iostream>
template<typename T>
void test(T *) {
std::cout << "ptr" << std::endl;
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
template<typename Ret, typename ...Args>
void test(Ret(*el)(Args...)) {
std::cout << "function ptr\n" << el(Args()...) << std::endl;
}
template<typename Char_T, typename Char_Traits, typename Ret, typename ...Args>
std::basic_ostream<Char_T, Char_Traits>& operator<<(
std::basic_ostream<Char_T, Char_Traits> &str, Ret(*el)(Args...)) {
return str << el(Args()...);
}
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test([]{return 5;}); // will not compile
}
I use gcc 7.3.1 with the version flag -std=c++14.
EDIT: Error message:
main.cc: In function ‘int main()’:
main.cc:25:23: error: no matching function for call to ‘test(main()::<lambda()>)’
test([]{return 5;});
^
main.cc:5:6: note: candidate: template<class T> void test(T*)
void test(T *) {
^~~~
main.cc:5:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘T*’ and ‘main()::<lambda()>’
test([]{return 5;});
^
main.cc:9:6: note: candidate: template<class T> void test(bool)
void test(bool) {
^~~~
main.cc:9:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: couldn't deduce template parameter ‘T’
test([]{return 5;});
^
main.cc:13:6: note: candidate: template<class Ret, class ... Args> void test(Ret (*)(Args ...))
void test(Ret(*el)(Args...)) {
^~~~
main.cc:13:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘Ret (*)(Args ...)’ and ‘main()::<lambda()>’
test([]{return 5;});
Your problem here is that Template Argument Deduction is only done on the actual argument passed to test. It's not done on all possible types that the argument could possibly converted to. That might be an infinite set, so that's clearly a no-go.
So, Template Argument Deduction is done on the actual lambda object, which has an unspeakable class type. So the deduction for test(T*) fails as the lambda object is not a pointer. T can't be deduced from test(bool), obviously. Finally, the deduction fails for test(Ret(*el)(Args...)) as the lambda object is not a pointer-to-function either.
There are a few options. You might not even need a template, you could accept a std::function<void(void)> and rely on the fact that it has a templated constructor. Or you could just take a test(T t) argument and call it as t(). T will now deduce to the actual lambda type. The most fancy solution is probably using std::invoke, and accepting a template vararg list.
Even though non-capturing lambdas have an implicit conversion to function pointers, function templates must match exactly for deduction to succeed, no conversions will be performed.
Therefore the easiest fix is to force the conversion with a +
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test(+[]{return 5;});
// ^
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
Template is not needed. In fact you overload functions, not templates. Replace it with
void test(bool) {
std::cout << "bool" << std::endl;
}
Now your sample will compile.

No matching function for call std::forward(const std::string &) with variadic arguments

I'm trying to make a movable wrapper to non-copyable, non-movable class, however I have a problem passing a const std::string variable to the constructor. The minimal example below produces following error:
#include <iostream>
#include <memory>
#include <string>
#include <utility>
struct X {
std::string x;
X(const std::string &x) : x(x) {}
X(const X &x) = delete;
X(X &&x) = delete;
};
struct Wrapper {
std::unique_ptr<X> x;
Wrapper(const Wrapper & wrapper) = delete;
Wrapper(Wrapper && wrapper) = default;
template<typename... Args>
Wrapper(Args&&... args) : x(std::make_unique<X>(std::forward(args)...)) {}
};
int main() {
const std::string XXX = "XXX";
Wrapper w{XXX};
std::cout << w.x->x << std::endl;
}
Error message here:
forwarding.cc:21:53: error: no matching function for call to 'forward'
Wrapper(Args&&... args) : x(std::make_unique<X>(std::forward(args)...)) {}
^~~~~~~~~~~~
forwarding.cc:26:13: note: in instantiation of function template specialization 'Wrapper::Wrapper<const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > &>' requested here
Wrapper w{XXX};
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/move.h:73:5: note: candidate template ignored: couldn't infer template argument '_Tp'
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/move.h:84:5: note: candidate template ignored: couldn't infer template argument '_Tp'
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^
1 error generated.
You need to explicitly pass template parameters to std::forward:
std::forward<Args>(args)...
This is because std::forward needs some way of knowing the "original value category" of args..., which is impossible through template argument deduction alone as args is always an lvalue.
Lvalues will deduced as lvalue references in the context of template argument deduction for forwarding references (as a special rule), so std::forward can do its job by looking at the types inside Args....

Constructing class with member tuple containing rvalue references

When trying to construct a class which is supposed to hold a tuple created by calling std::forward_as_tuple I ran into the following error when compiling with clang(187537) and libc++:
/usr/include/c++/v1/tuple:329:11: error: rvalue reference to type 'int' cannot
bind to lvalue of type 'int'
: value(__t.get())
^ ~~~~~~~~~
/usr/include/c++/v1/tuple:447:8: note: in instantiation of member function
'std::__1::__tuple_leaf<0, int &&, false>::__tuple_leaf' requested here
struct __tuple_impl<__tuple_indices<_Indx...>, _Tp...>
^
tuple.cpp:31:5: note: in instantiation of function template specialization
'make_foo2<int>' requested here
make_foo2(1 + 1);
^
In file included from tuple.cpp:2:
/usr/include/c++/v1/tuple:330:10: error: static_assert failed "Can not copy a
tuple with rvalue reference member"
{static_assert(!is_rvalue_reference<_Hp>::value, "Can not copy ...
I was able to work around the above error by declaring the return type differently, but, from my understanding, it should have the same semantics so I would not expect it to stop the error. In the below code make_foo is the workaround which does not error out and make_foo2 causes the above error. I am able to successfully compile both versions using gcc 4.8.1 and the version of clang at coliru.
#include <utility>
#include <tuple>
template<class Tuple>
struct foo
{
Tuple t;
foo(Tuple &&t) : t(std::move(t)) { }
};
template<class... Args>
using ForwardedTuple = decltype(std::forward_as_tuple(std::forward<Args>(std::declval<Args>())...));
template<class... Args>
foo<ForwardedTuple<Args...>> make_foo(Args&&... args)
{
return {std::forward_as_tuple(std::forward<Args>(args)...)};
}
template<class... Args>
auto make_foo2(Args&& ...args) ->
decltype(foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...)))
{
return foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...));
}
int main()
{
make_foo(1 + 1);
make_foo2(1 + 1);
}
What is the difference between the above make_foo functions and is make_foo2 incorrect?
Thanks.
Looks like you return foo<> from make_foo2. But foo doesn't have move constructor generated (Compiler won't generate it). Therefore copy constructor is called and compilation fails because of that.

template argument deduction/substitution failed, when using std::function and std::bind

I have a compile error when using std::function in a templated member function, the following code is a simple example:
#include <functional>
#include <memory>
using std::function;
using std::bind;
using std::shared_ptr;
class Test {
public:
template <typename T>
void setCallback(function<void (T, int)> cb);
};
template <typename T>
void Test::setCallback(function<void (T, int)> cb)
{
// do nothing
}
class TestA {
public:
void testa(int a, int b) { }
};
int main()
{
TestA testA;
Test test;
test.setCallback(bind(&TestA::testa, &testA, std::placeholders::_1, std::placeholders::_2));
return 0;
}
And come with the following compile error:
testtemplate.cpp: In function ‘int main()’:
testtemplate.cpp:29:92: error: no matching function for call to
‘Test::setCallback(std::_Bind_helper)(int, int),
TestA, const std::_Placeholder<1>&, const
std::_Placeholder<2>&>::type)’
testtemplate.cpp:29:92: note: candidate is: testtemplate.cpp:10:7:
note: template void Test::setCallback(std::function)
testtemplate.cpp:10:7: note: template argument
deduction/substitution failed:
testtemplate.cpp:29:92: note: ‘std::_Bind(TestA*, std::_Placeholder<1>,
std::_Placeholder<2>)>’ is not derived from ‘std::function’
I'm using C++11 and g++ 4.7
To figure out the problem let separate statements:
auto f = bind(&TestA::testa, &testA, _1, _2); // OK
test.setCallback(f); // <<--- Error is here
setCallback needs to know type of T and it can't deduce it from f, so give it a type
test.setCallback<TYPE>(f); // TYPE: int, float, a class, ...
You can make type deduction work with some variant of:
template<typename CALLBACK>
void setCallback(CALLBACK cb) {
typedef CALLBACK::first_argument_type T;
static_assert(is_same_type<CALLBACK,function<void(T,int)>>::value);
...
}
This way CALLBACK can be determined by looking at the argument. It might get into trouble if bind doesn't actually return a std::function but rather something that can be cast as one. I'm not sure.