Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deducing template parameter from functor argument type [duplicate]

Tags:

c++

templates

qt

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.

like image 277
George Hilliard Avatar asked Nov 20 '25 14:11

George Hilliard


1 Answers

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;
}
like image 97
Barry Avatar answered Nov 22 '25 04:11

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!