Reputation: 23921
I have a the following template and specialization (this code is not correct, but hopefully demonstrates my intent well enough):
template <typename T> widget &&make_widget(T &&val) { // (1)
return std::move(widget(std::forward<T>(val)));
}
template <> widget &&make_widget(void) { // (2)
return std::move(widget());
}
The intent is to have a factory function that can be called like this:
make_widget(arbitrary_function());
And have it choose the default constructor if arbitrary_function
returns void
.
However, with clang 3.7 I get the following error:
error: no function template matches function template specialization 'make_widget'
pointing to the line of (2). How can I implement this correctly?
Upvotes: 3
Views: 4273
Reputation: 118330
As it's been pointed out, the problem is not really in the template specialization, but the usage of a void expression.
I can, however, suggest an alternative syntax that uses an intermediate lambda, and a helper template, to achieve the same results at a cost of a little bit of negligible extra usage syntax.
#include <functional>
class widget {
public:
widget();
widget(int);
};
template <typename T> widget &&do_make_widget(T &&val) { // (1)
return std::move(widget(std::forward<T>(val)));
}
widget &&do_make_widget() { // (2)
return std::move(widget());
}
template <typename T>
class do_make_widget_invoke {
public:
template<typename functor_type>
static auto invoke(functor_type &&functor)
{
return do_make_widget(functor());
}
};
template <>
class do_make_widget_invoke<void> {
public:
template<typename functor_type>
static auto invoke(functor_type &&functor)
{
functor();
return do_make_widget();
}
};
template<typename functor_type>
auto make_widget(functor_type &&functor)
{
return do_make_widget_invoke<decltype(functor())>
::invoke(std::forward<functor_type>(functor));
}
Testing the above with g++ 5.1.1 in -std=c++14 mode, I seem to get the right results with the following syntax, which is pretty much what you're trying to accomplish:
int foobar()
{
return 0;
}
int main()
{
make_widget([]{ return foobar(); });
}
and:
void foobar()
{
}
int main()
{
make_widget([]{ return foobar(); });
}
Upvotes: 1
Reputation: 119164
You can't do this. It's impossible to create a function that has a parameter of type void
. What you can do is make the function variadic, like make_unique
.
template <typename... T>
widget make_widget(T&&... val) {
return widget(std::forward<T>(val)...);
}
Then if you want to do something like
auto w = make_widget(void_function());
there is nothing stopping you from just doing instead:
void_function();
auto w = make_widget();
or even, if you really need it to be one statement for some reason,
auto w = (void_function(), make_widget());
Three further notes:
std::move
in the return statement since the result of calling the constructor is already an rvalue.make_widget
function if forwarding its arguments to the widget
constructor is the only thing it does. Note that make_unique
takes care of calling new
for you, and that make_tuple
deduces the template arguments for the tuple
itself. Your make_widget
function doesn't do anything like that.Upvotes: 3