Que*_*tin 15 c++ templates function-pointers language-lawyer generic-lambda
这有两件事可行.我们可以实例化一个转发函数模板来获取一个带左值的函数指针:
template <class T>
void f(T &&) {}
void(*p)(int &) = f; // Cool!
Run Code Online (Sandbox Code Playgroud)
我们还可以将带有左值的非捕获通用lambda转换为带左值的函数指针:
auto l = [](auto &) { };
void (*lp)(int &) = l; // Still cool!
Run Code Online (Sandbox Code Playgroud)
但显然GCC和Clang都没有将转发通用lambda转换为带左值的函数指针:
auto l = [](auto &&) { };
void (*lp)(int &) = l; // Not cool!
Run Code Online (Sandbox Code Playgroud)
GCC产出:
<source>:9:21: error: invalid user-defined conversion from '<lambda(auto:1&&)>' to 'void (*)(int&)' [-fpermissive]
void (*lp)(int &) = l;
^
Run Code Online (Sandbox Code Playgroud)
Clang输出:
<source>:9:8: fatal error: no viable conversion from '(lambda at <source>:7:10)' to 'void (*)(int &)'
void (*lp)(int &) = l;
^ ~
<source>:7:10: note: candidate template ignored: could not match 'type-parameter-0-0 &&' against 'int &'
auto l = [](auto &&) { };
^
Run Code Online (Sandbox Code Playgroud)
尽管可以从转发lambda 获取一个带左值的成员函数指针,但这一切都是这样的:
auto lmp = &decltype(l)::operator()<int &>;
template <class...>
struct check;
check<decltype(lmp)> c;
Run Code Online (Sandbox Code Playgroud)
... void (<lambda(auto:1&&)>::*)(int&) const按预期输出类型.
我认为参考折叠规则是任何模板实例化所固有的,并且期望它能够工作.Clang和GCC都有错误,或标准实际上没有提供?
TL; DR:这是根据标准的指定行为.模板参数推导有一个特殊的规则,用于在获取函数模板的地址时推导模板参数,允许转发引用按预期工作.转换函数模板没有这样的规则.
注意:这看起来只是一个尚未编写提案的领域.如果有人为此撰写提案,似乎可能会在将来工作.
...... 对于没有lambda-capture的通用lambda,闭包类型有一个指向函数的转换函数模板.转换函数模板具有相同的发明模板参数列表,并且指向函数的指针具有与函数调用操作符模板相同的参数类型.指向函数的指针的返回类型应该表现为它是一个decltype-specifier,表示相应函数调用操作符模板特化的返回类型.
重点补充
这表明必须以一对一的方式复制模板参数和函数参数类型:
// simplified version of the example in [expr.prim.lambda]/8
struct Closure {
template <typename T>
void operator()(T&& t) const {
/* ... */
}
template <typename T>
static void lambda_call_operator_invoker(T&& t) {
Closure()(std::forward<T>(t));
}
// Exactly copying the template parameter list and function parameter types.
template <typename T>
using fn_type = void(*)(T&&);
// using fn_type = void(*)(T); // this compiles, as noted later
template <typename T>
operator fn_type<T>() const {
return &lambda_call_operator_invoker<T>;
}
};
Run Code Online (Sandbox Code Playgroud)
这无法在Clang,GCC和MSVC的所有三个上进行编译,这当然是令人惊讶的,因为我们期望参数崩溃发生在T&&争论上.
但是,该标准不支持这一点.
标准的重要部分是[temp.deduct.funcaddr](使用函数模板的地址推导模板参数)和[temp.deduct.conv](推导转换函数模板参数).关键的是,[temp.deduct.type]特别提及[temp.deduct.funcaddr],但不提及[temp.deduct.conv].
标准中使用的一些术语:
类似地,如果P具有包含(T)的形式,则将P 的相应参数类型列表([dcl.fct])的每个参数类型P i与相应参数的相应参数类型A i进行比较 - A的类型列表.如果P和A是在获取函数模板的地址([temp.deduct.funcaddr])时从演绎中产生的函数类型,或者是从函数声明中推导出模板参数([temp.deduct.decl] ])和P i和A i分别是P 和A 的顶级参数类型列表的参数,如果它是转发参考([temp.deduct.call])则调整P i并且A i是左值参考,在这种情况下,P i的类型被改变为模板参数类型(即,
T&&简单地改变T).
重点补充
这特别要求获取函数模板的地址,使转发引用正常工作.转换函数模板没有类似的引用.
重新访问前面的示例,如果我们fn_type改为void(*)(T),那就是标准中描述的相同操作.