【问题标题】:Deducing template parameter from functor argument type [duplicate]从函子参数类型推导出模板参数
【发布时间】:2015-09-19 05:44:40
【问题描述】:

我有以下代码,用于接受 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::anyqvariant_cast&lt;T&gt;() 获取原始对象一样)。

如何捕获传递给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();
});

当然,这更难看。

【问题讨论】:

    标签: c++ qt templates


    【解决方案1】:

    基本上你想要的是:给定一个函子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;
    }
    

    【讨论】:

    • 完美!这是一个神秘的using 声明;我希望 C++ 有类似 Rust 的 where 子句。那好吧。是否有一个规范的 function_traits 或者它只是人们写的东西,因为它很有用?
    • @Barry function_traits 是一个不错的实用程序。我想你可能有兴趣知道 std::bind(f, std::placeholders::_1) 失败
    • @RichardHodges 你不能推断出函数模板的参数类型,对吧?你必须提供它。
    • @Barry 我认为可以做到,decltype(bind(F, Args&&...)) 给出了绑定对象的类型和函数对象的类型/类。然后必须有一些 mpl 逻辑来使用 std::is_placeholder 找到第 n 个占位符并将它们映射回 F 的参数。我现在不想做的事情,现在是午夜半点,但值得深思?
    【解决方案2】:
    map<QByteArray>(someFuncReturningQVariant(), [](auto&& array){
      return array.toString();
    });
    

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

    还要注意 -&gt;QString 在 C++11 或 14 中是多余的。

    另外,要知道QVariant 不是适合映射的类型。在您的地图函数外部进行显式转换。

    QVariant 映射到外部optional&lt;T&amp;&gt;(或T*),并返回optional&lt;std::result_of_t&lt;F(T&amp;)&gt;&gt;(或QVariant,如果您喜欢丢弃信息)。

    【讨论】:

    • 明白了。我早些时候偶然发现了这一点并忘记了它。 Lambda 类型的演绎非常酷。最后,我必须返回 QVariant,因为我正在实现一个 Qt 代理模型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    • 1970-01-01
    • 2019-09-12
    • 2021-05-23
    • 2018-12-05
    相关资源
    最近更新 更多