【问题标题】:how can i call template function not using explicit type in C++?如何在 C++ 中调用不使用显式类型的模板函数?
【发布时间】:2021-06-03 04:07:55
【问题描述】:

当我编写如下 C++ 代码时:

#include <iostream>
#include <memory>
#include <functional>

template <typename T>
struct Node
{
    T data;
    std::shared_ptr<T> left;
    std::shared_ptr<T> right;

    Node(const T& data)
        :data(data)
    {}
};

template <typename T>
void PreorderTraverse(std::shared_ptr<Node<T>>& root, 
                      std::function<void(std::shared_ptr<Node<T>>&)> callback = nullptr)
{
    callback(root);
}

int main()
{
    using namespace std;

    auto root = make_shared<Node<int>>(5);
    // Why must write template type explicitly?
    //PreorderTraverse(root, [](shared_ptr<Node<int>>& node)->void { cout << node->data << endl; });

    PreorderTraverse<int>(root, [](shared_ptr<Node<int>>& node)->void { cout << node->data << endl; });

    //...

    return 0;
}

我不能像这样调用 PreorderTraverse 来执行

PreorderTraverse(root, [](shared_ptr& node)->void { cout data

也许编译器无法推断实例类型,那么我应该如何修改此代码以使编译器能够像上面的评论一样推断调用“PreorderTraverse”?

【问题讨论】:

  • 对不起,我有一些错字。执行 -> 形式。大声笑

标签: c++ templates lambda


【解决方案1】:

PreorderTraverse 正在使用 std::function,而您正在传递一个 lambda。不幸的是,在 template argument deduction 中不会考虑隐式转换(从 lambda 到 std::function),它无法在第二个函数参数 callback 上推导 T

类型推导不考虑隐式转换(除了上面列出的类型调整):这是重载解析的工作,稍后会发生。

您可以使用std::type_identity(C++20 起)将callback 排除在推导之外。

template <typename T>
void PreorderTraverse(std::shared_ptr<Node<T>>& root,                                    
                      std::function<void(std::shared_ptr<Node<std::type_identity_t<T>>>&)> callback = nullptr)
//                                                            ^^^^^^^^^^^^^^^^^^^^^ ^
{
    callback(root);
}

LIVE

或者添加另一个模板参数并停止使用std::function。例如

template <typename T, typename F>
void PreorderTraverse(std::shared_ptr<Node<T>>& root, 
                      F callback)
{
    callback(root);
}

LIVE

【讨论】:

  • 谢谢!我从你们的 cmets 身上学到了很多东西。
【解决方案2】:

基本上,模板过于复杂,编译器无法推断。更具体地说,第二个参数是一个闭包(由 lambda 表达式创建的匿名类型),而不是 std::function - 它与将第一个 shared_ptr 作为参数的 std::function 兼容,但推论太复杂了供编译器根据标准规则制定的链。

一种解决方案是将第二个参数设为单独的类型:

template <typename T, typename F>
void PreorderTraverse(std::shared_ptr<Node<T>>& root, F callback) {
    callback(root);
}

【讨论】:

  • “太复杂了,编译器无法推断”这绝不是一回事。
  • 标准中有一些实际规则规定了允许的内容和工作方式。如果您不了解它们或不想在回答中解释它们,那很好......但不要随意编造一些“它太复杂......”的理由。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-11
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
相关资源
最近更新 更多