I have the following code that is meant to accept a Qt QVariant and apply a functor if the variant contains a value:
template<typename T, typename Functor>
inline QVariant map(const QVariant& v, Functor f)
{
return v.isValid()
? QVariant{f(qvariant_cast<T>(v))}
: v;
}
My problem is that the compiler cannot deduce the type of T when I invoke this function as
map(someFuncReturningQVariant(), [](const QByteArray& array) -> QString {
return array.toString();
});
The compiler complains (cleaned up from the original error, which has longer type names):
error: no matching function for call to `map(QVariant, <lambda(const QByteArray&)>)`
note: candidate is:
template<class T, class Functor> QVariant map(const QVariant&, Functor).
note: template argument deduction/substitution failed:
couldn't deduce template parameter 'T'
This is because QVariant erases the type of the object it contains at runtime. (It still knows it internally, much like boost::any
, and qvariant_cast<T>()
gets the original object back).
How can I capture the type of the variable passed to the Functor
and use it elsewhere? Alternatively, how can I specify that Functor
takes a parameter of type T
? (I suspect these are actually the same question, or that they at least have the same answer.)
Note that my approach works fine with std::optional
, because the types are not erased:
using std::experimental::optional;
template<typename T, typename Functor>
inline auto map(const optional<T>& v, Functor f) -> optional<decltype(f(*v))>
{
return v ? optional<decltype(f(*v))>{f(*v)}
: v;
}
Also note that the QVariant code works fine if I manually specify the type:
map<QByteArray>(someFuncReturningQVariant(), [](const QByteArray& array) -> QString {
return array.toString();
});
But of course, this is much uglier.