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.
Basically what you want is: given a functor, F, identify the decayed type of its first argument.
To that end, we need function_traits, with which we can do:
template <typename F>
using first_arg = std::decay_t<typename function_traits<F>::template arg<0>::type>;
Which we use:
template<typename Functor>
inline QVariant map(const QVariant& v, Functor f)
{
using T = first_arg<Functor>;
return v.isValid()
? QVariant{f(qvariant_cast<T>(v))}
: v;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With