Reputation: 205
I am trying to write a class which is able to call a lambda with no arguments later in time. I was expecting C++17 class template argument deduction to avoid the need for a factory function. However, trying to instantiate an object without specifying the type fails. I am fine using the factory function but I would like to understand why this happens.
I am using VC++2017, with the C++17 toolset enabled. Is this expected behavior? Why? Can the factory function be avoided or is it needed due to different type deduction rules for template functions and template classes? Any help would be appreaciated.
template <typename F>
class WillInvoke
{
public:
WillInvoke(std::decay_t<F> f) : f(std::move(f)) { }
void CallNow() { f(); }
private:
std::decay_t<F> f;
};
template <typename F>
WillInvoke<F> make_WillInvoke(F && f)
{
return WillInvoke<F>(std::forward<F>(f));
}
int main()
{
// OK
auto w = make_WillInvoke([](){ std::cout << "Hello World"; });
w.CallNow();
// Won't compile
WillInvoke w2([](){ std::cout << "Hello World"; }); // No instance of constructor matches argument list
w2.CallNow();
}
Upvotes: 1
Views: 87
Reputation: 41750
This is because a member type alias like std::decay<T>::type
is not deductible.
template<typename T>
void call_me(std::decay_t<T>) {}
// won't work :(
// call_me(1);
I don't think your class should decay the type. Instead, your class should state that it requires an object type and move the decay into the make function:
template <typename F> // requires std::is_object_v<F>
class WillInvoke
{
static_assert(std::is_object_v<F>,
"WillInvoke requires F to be an object type"
);
public:
WillInvoke(F f) : f(std::move(f)) { }
void CallNow() { f(); }
private:
F f;
};
template <typename F>
auto make_WillInvoke(F && f) -> WillInvoke<std::decay_t<F>>
{
return WillInvoke<std::decay_t<F>>(std::forward<F>(f));
}
The nice thing is that in C++20, you can uncomment the requires and let the compiler enforce that in the call site.
Upvotes: 5