为什么不能使用可选的UnaryPredicate参数创建模板函数?

时间:2019-02-21 06:06:42

标签: c++ templates template-deduction

我正在尝试创建带有可选参数的模板化函数,并且在理解为什么编译失败时遇到了麻烦。这是我的测试(人为)代码:

#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 <<“ NumElements =” << GetCountIf(v)<<'\ n';                                    ^ ~~~~~~~~~ main.cpp:5:5:注意:候选模板被忽略:无法推断模板参数   'UnaryPredicate'int GetCountIf(std :: vector v,UnaryPredicate   pred = {返回true; }){       产生了^ 1个错误。

当编译器遇到仅具有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)

2 个答案:

答案 0 :(得分:7)

请注意,函数参数的默认值不会用于模板参数的模板参数推导;导致模板参数推导失败,无法推导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)
}

另一方面,如果您为模板参数std::function<bool(T)>指定了默认值UnaryPredicate,那么如果{{1 }}没有明确指定或推论。

答案 1 :(得分:0)

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

或者,您可能会创建重载:

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(); // :-)
}