从仿函数参数类型中推导出模板参数

时间:2015-07-01 21:25:55

标签: c++ qt templates

我有以下代码用于接受Qt QVariant并在变量包含值时应用仿函数:

template<typename T, typename Functor>
inline QVariant map(const QVariant& v, Functor f)
{
    return v.isValid()
            ? QVariant{f(qvariant_cast<T>(v))}
            : v;
}

我的问题是,当我将此函数调用为

时,编译器无法推断出T的类型
map(someFuncReturningQVariant(), [](const QByteArray& array) -> QString {
    return array.toString();
});

编译器抱怨(从原始错误清除,其中包含更长的类型名称):

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'

这是因为QVariant在运行时删除了它包含的对象的类型。 (它仍然在内部知道它,就像boost::any一样,而qvariant_cast<T>()得到原始对象。

如何捕获传递给Functor 的变量类型并在其他地方使用?或者,如何指定Functor采用T类型的参数? (我怀疑这些问题实际上是同一个问题,或者他们至少有相同的答案。)

请注意,我的方法适用于std::optional,因为类型不会被删除:

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;
}

另请注意,如果我手动指定类型,QVariant代码可以正常工作:

map<QByteArray>(someFuncReturningQVariant(), [](const QByteArray& array) -> QString {
    return array.toString();
});

但当然,这更加丑陋。

2 个答案:

答案 0 :(得分:3)

基本上你想要的是:给定一个仿函数F,识别它的第一个参数的衰变类型。

为此,我们需要function_traits,我们可以做到:

template <typename F>
using first_arg = std::decay_t<typename function_traits<F>::template arg<0>::type>;

我们使用:

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;
}

答案 1 :(得分:2)

map<QByteArray>(someFuncReturningQVariant(), [](auto&& array){
  return array.toString();
});

是C ++ 14的方法:不要在lambda中指定类型,而是(仅)指定模板参数。

另请注意,{C} 11或14中->QString是多余的。

或者,知道QVariant不适合映射。将转换明确地放在地图函数的外部。

QVariant映射到optional<T&>外部(或T*),如果您愿意丢弃信息,则返回optional<std::result_of_t<F(T&)>>(或QVariant )。