Extracting a template parameter from an arguctor type
I have the following code, which is 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 infer the type of T when I call this function as
map(someFuncReturningQVariant(), [](const QByteArray& array) -> QString {
return array.toString();
});
The compiler complains (cleared of 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, how boost::any
, and it qvariant_cast<T>()
returns the original object).
How can I capture the type of the variable passed toFunctor
and use it elsewhere? Alternatively, how can I specify what Functor
the type parameter is taking T
? (I suspect this is actually the same question, or that they at least have the same answer.)
Note that my approach works great 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.
source to share
Basically what you want: define a functor F
, define the decomposed type of its first argument.
For this we need function_traits
, with which we can:
template <typename F>
using first_arg = std::decay_t<typename function_traits<F>::template arg<0>::type>;
What 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;
}
source to share
map<QByteArray>(someFuncReturningQVariant(), [](auto&& array){
return array.toString();
});
is the C ++ 14 way: don't specify the type in the lambda, but rather as (only) a template parameter.
Also note which ->QString
is redundant in C ++ 11 or 14.
Alternatively, know what is QVariant
not a matchable type. Make the cast explicitly external to your mapping function.
Match QVariant
with a optional<T&>
from outside (or a T*
) and return optional<std::result_of_t<F(T&)>>
(or a QVariant
if you like to discard information).
source to share