默认 lambda 作为函数的模板化参数

Default lambda as templated parameter of a function

考虑以下代码

template<bool b, typename T> void foo(const T& t = []() {}) {
  // implementation here
}

void bar() {
  foo<true>([&](){ /* implementation here */ }); // this compiles
  foo<true>(); // this doesn't compile
}

在无法编译的情况下,我得到以下错误:

error C2672: 'foo': no matching overloaded function found
error C2783: 'void foo(const T&)': could not deduce template argument for 'T'

我想我想要实现的目标很明确:让 foo 在有和没有客户端提供的 lambda 的情况下被调用。编译器为MSVC++2017版本15.4.4工具集v141.

考虑直接重载它:

template <bool b>
void foo(void) {
  foo([](){});
}

参见CppReference

Non-deduced contexts

4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:

Type template parameter cannot be deduced from the type of a function default argument: template 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)
}

默认函数参数不是模板参数推导过程的一部分。引用 [temp.deduct.partial]/3:

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • In the context of a function call, the types used are those function parameter types for which the function call has arguments. 141

141) Default arguments are not considered to be arguments in this context; they only become arguments after a function has been selected.

该项目符号和注释表明,由于您没有在对 foo 的调用中为 t 提供参数,因此无法推导出类型 T。只有在选择调用函数时才会考虑默认的 lambda 参数,而不是之前。

正如所有其他人指出的那样,解决方案是提供一个不带参数的重载,它将使用您想到的默认 lambda 调用模板化的重载。

编译器使用参数 passed 来推断模板类型。如果没有参数,那么编译器如何推断模板类型?

您可以在此处使用 重载 而不是默认参数。

重载的无参数函数可以简单地调用带"default"参数的函数:

template<bool b, typename T> void foo(const T& t) {
  // implementation here
}

template<bool b> void foo() {
  foo<b>([]() {});
}

你想说一些毫无意义的话。您要求编译器从您的参数中猜测 T,但是您没有提供任何参数。

以下代码可以编译,并且可以执行您想要的操作:

template<bool b, typename T> void foo(const T& t) {
  // implementation here
}

template<bool b> void foo() {
  foo<b>([]() {}); // Call actual implementation with empty lambda
}

void bar() {
  foo<true>([&](){ /* implementation here */ }); // this compiles
  foo<true>(); // this now compiles as well
}

另一种(非常有效的)方式 - 默认 T 为空函子。

// no_op is a function object which does nothing, regardless of how many
// arguments you give it. It will be elided completely unless you compile with
// -O0
struct no_op 
{ 
    template<class...Args>
    constexpr void operator()(Args&&...) const {} 
};

// foo defaults to using a default-constructed no_op as its function object
template<bool b, typename T = no_op> void foo(T&& t = T()) 
{    
  // implementation here
    t();
}

void bar() {
  foo<true>([&](){ std::cout << "something\n"; }); // this compiles
  foo<true>(); // this now compiles
}