SSCCE:
#include <functional>
using std::function;
using std::forward;
template<typename ToType, typename... FromTypes>
ToType construct(FromTypes&&... fromTypes) {
return ToType(forward<FromTypes>(fromTypes)...);
}
class Maybe {
public:
Maybe() : m_value(42.0f) {}
template<typename Function>
auto apply(Function function) const -> decltype(function(std::declval<float>())) {
return function(value());
}
private:
float const& value() const {
return m_value;
}
float m_value;
};
int main() {
Maybe a;
a.apply(construct<int, float>);
return 0;
}
Gives the error:
test.cpp: In instantiation of ‘decltype (function(declval<float>())) Maybe::apply(Function) const [with Function = int (*)(float&&); decltype (function(declval<float>())) = int]’:
test.cpp:31:32: required from here
test.cpp:17:28: error: invalid initialization of reference of type ‘float&&’ from expression of type ‘const float’
return function(value());
^
From the error message, it's obviously a problem with the fact that value()
returns a const&
.
The key point here, is that the type isn't being deduced on line 17, where value is being passed to it. The type is being assigned when the construct
function is passed to apply
on line 31.
I specified the wrong type to the template of construct
. construct<int, float>
. If I use construct<int, float const&>
it functions just fine.
However, this is cumbersome and requires knowledge of the implementation of apply
. And it will also never ever bind an lvalue, because T
and T&&
are different types. (Because of the lack of type deduction.)
Is there a way to have a function I can pass to another function and have type deduction occur at the site where it is called, so I can have perfect forwarding happen more or less transparently for the callers? Or is there another way to achieve this end that doesn't leak complexity to the caller?
How about this?
#include <functional>
using std::function;
using std::forward;
template<typename ToType>
class construct
{
public:
template<typename... FromTypes>
ToType operator()(FromTypes&&... fromTypes) {
return ToType(forward<FromTypes>(fromTypes)...);
}
};
class Maybe {
public:
Maybe() : m_value(42.0f) {}
template<typename Function>
auto apply(Function function) const -> decltype(function(std::declval<float>())) {
return function(value());
}
private:
float const& value() const {
return m_value;
}
float m_value;
};
int main() {
Maybe a;
a.apply(construct<int>());
return 0;
}
You only have to specify the type to which you want to convert to which obviously cannot be deduced in the context you have given.