【问题标题】:Why can't I create a template function with an optional UnaryPredicate argument?为什么我不能使用可选的 UnaryPredicate 参数创建模板函数?
【发布时间】:2019-07-14 23:13:45
【问题描述】:

我正在尝试使用可选参数创建一个模板化函数,但我无法理解编译失败的原因。这是我的测试(人为)代码:

#include <iostream>
#include <vector>

template <class UnaryPredicate>
int GetCountIf(std::vector<int> v, UnaryPredicate pred = [](auto) { return true; }) {
  int count=0;
  for (auto i: v) {
    if (pred(i)) {
      count++;
    }
  }
  return count;
}

int main() {
  auto v = std::vector<int>{0, 1, 2, 3, 4, 5};
  std::cout << "NumOddElements=" << GetCountIf(v, [](auto val) { return (val % 2 == 1); }) << '\n';
  // std::cout << "NumElements=" << GetCountIf(v) << '\n';
}

仅当我使用两个参数调用 GetCountIf() 时,代码才会编译。如果我尝试只传递 1 个参数,编译会失败并出现以下错误:

main.cpp:18:34: 错误:没有匹配的函数调用“GetCountIf”
std::cout

当编译器遇到只有 1 个参数的 GetCountIf 调用时,为什么它不能推断出可选 lambda 的类型?如果我像这样明确指定谓词的类型,它会起作用:

template <typename T, class UnaryPredicate = std::function<bool(T)>>
int GetCountIf(std::vector<T> v, UnaryPredicate pred = [](T) { return true;}) {
  ...
}

为什么会这样?

(我使用的是 C++14)

【问题讨论】:

    标签: c++ templates template-argument-deduction


    【解决方案1】:

    注意函数参数的默认值不会用于模板参数的模板实参推导;导致模板参数推导失败,无法推导UnaryPredicate的类型。

    non-deduced contexts

    在以下情况下,用于构成 P 的类型、模板和非类型值不参与模板参数推导,而是使用在其他地方推导或显式指定的模板参数。如果模板参数仅在非推导上下文中使用且未显式指定,则模板参数推导失败。

    4) 函数参数类型中使用的模板参数 具有在调用中使用的默认参数的参数 正在为哪个参数推导:

    template<typename T, typename F>
    void f(const std::vector<T>& v, const F& comp = std::less<T>());
    std::vector<std::string> v(3);
    f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue
          // P1/A1 deduced T = std::string
          // P2 = const F&, A2 = std::less<std::string> rvalue
          // P2 is non-deduced context for F (template parameter) used in the
          // parameter type (const F&) of the function parameter comp,
          // that has a default argument that is being used in the call f(v)
    

    类型模板参数不能从函数的类型推导出来 默认参数:

    template<typename T> void f(T = 5, T = 7);
    
    void g()
    {
        f(1);     // OK: calls f<int>(1, 7)
        f();      // error: cannot deduce T
        f<int>(); // OK: calls f<int>(5, 7)
    }
    

    另一方面,如果您为模板参数UnaryPredicate 指定默认值std::function&lt;bool(T)&gt;,那么如果UnaryPredicate 的参数未明确指定或未明确指定,它将用作UnaryPredicate 的类型推导出来。

    【讨论】:

    • bool(T) 的默认参数可以解决问题,并且不需要 std::function 的类型擦除。
    • 谢谢,类型推断总是让我头晕目眩。 @user975989 我更喜欢 bool(T) 而不是使用 std::function 包装器。谢谢你的建议。
    【解决方案2】:

    默认参数确实参与了类型的推断,因此您观察到的行为。

    作为替代方案,您可以创建重载:

    template <class UnaryPredicate>
    int GetCountIf(const std::vector<int>& v, UnaryPredicate pred) {
        int count = 0;
        for (auto i: v) {
            if (pred(i)) {
              count++;
            }
        }
        return count;
    }
    
    int GetCountIf(const std::vector<int>& v) {
        return GetCountIf(v, [](auto) { return true; }); // return v.size(); // :-)
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-20
      • 1970-01-01
      • 2019-11-16
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      • 2015-09-04
      • 1970-01-01
      • 2010-10-31
      相关资源
      最近更新 更多