下面的代码合法吗?
template <auto Lambda>
struct A {};
int main () {
auto lmb = [](int i){return i*i;};
A<lmb> a;
return 0;
}我注意到g++可以很好地编译它,而clang++返回error: a non-type template parameter cannot have type '(lambda at main.cpp:...)'。
发布于 2020-06-11 20:25:30
是否可以将lambda用作非类型模板参数?
是的,对于已经实现了P0732R2 - Class types in non-type template parameters但clang++还没有实现它的实现。
来源:https://en.cppreference.com/w/cpp/compiler_support
请注意,lambda至少需要为constexpr (默认情况下):
当此说明符不存在时,如果函数调用运算符恰好满足所有
constexpr函数要求,则它无论如何都将为constexpr。
但是,您可以添加constexpr来获取lambda本身的错误,而不是将其用作模板参数。注意:您还可以将其指定为consteval,以使其作为非类型模板参数工作。
有状态的lambda可以是constexpr
constexpr auto lmb1 = [](int i) {
static int x = 0;
return i*i + ++x;
};而通过引用捕获的λ,或通过拷贝和突变(mutable)捕获的λ则不能。不过,通过复制constexpr进行捕获是可以的。
泛型lambda也可以是constexpr:
constexpr auto gen_lmb = []<typename T>(T& val) {
val += val;
return val;
};
template <auto Lambda>
struct A {
template<typename T>
void doit(T&& arg) {
std::cout << Lambda(arg) << '\n';
}
};
//...
A<gen_lmb> ginst;
int v = 1000;
ginst.doit(v);
ginst.doit(std::string("foo "));
std::cout << v << '\n';2000
foo foo
2000发布于 2020-06-11 21:02:14
[temp.arg.nontype]/1
如果模板参数的类型T包含占位符类型(dcl.spec.auto)或推导的类类型的占位符(dcl.type.class.deduct),则参数的类型是为所发明的声明中的变量x推导的类型
T x=模板参数;
如果推导出的参数类型不允许用于模板参数声明(temp.param),则程序的格式不正确。
因此,规则是由[temp.param]/6设置的
非类型模板参数应具有以下类型之一(可能是cv限定的):...
(6.1)结构类型...
structural type的规则是:--我的重点是--
(7.1)标量类型,或
(7.2)左值引用类型,或
(7.3)使用以下属性一个文本类类型:
(7.3.1)所有基类和非静态数据成员都是公共和非可变,并且
(7.3.2)所有基类和非静态数据成员的类型都是结构类型或其(可能是多维)数组。
因为lambda没有基类,所以唯一的要求是它必须是一个literal class type ([basic.types]),它包括:
(10.5.2) ...闭包类型(expr.prim.lambda.closure) ...
结构类型的数据成员也应该是结构类型,这适用于本例中的lambda捕获,只要它的所有成员都是公共和non-mutable.
@Nicol Bolas ,带有捕获的lambda,即使constexpr文本类型捕获,标准也不强制将捕获作为公共字段进行管理。
底线是,在C++20中,没有捕获的常量表达式作为模板非类型参数是合法的(基于上面提到的 )。
另请参见an answer by @Barry to a similar question。
在代码compiles with gcc下面,但我从Nicol Bolas的评论中了解到,并不是所有的情况都得到了规范的保证(或者更糟糕的是,所有的情况都不是规范保证的?)。
假设我们有:
template <auto T> struct A {};
struct B {};
struct C {
~C(){}
};文本类型lambdas,作为模板参数的合法的:
// compiles in gcc and should be ok by the spec as of [basic.types]/10.5.2
A<[](){}> a; // compiler deduces the anonymous lambda to be constexpr
auto lmb1 = [](){};
// same as above
A<lmb1> a1;
// compiler deduces lmb1 above to be constexpr
// same as it will deduce the following:
B b {};
A<b> here_i_am;Lambda,,由gcc编译为模板参数,但正如Nicol Bolas在评论中所说的那样-规范不能保证它们是文字类型:
const int i = 0;
constexpr auto lmb2 = [i](){};
// compiles in gcc but is not guaranteed by the spec
A<lmb2> a2;
constexpr auto lmb3 = [b](){}; // B is literal
// compiles in gcc but is not guaranteed by the spec
A<lmb3> a3;非文字类型lambdas,不是合法的作为模板参数:
const int j = 0;
// below doesn't compile: <lambda()>{j} is not a constant expression
constexpr auto lmb4 = [&j](){}; // local reference - not constexpr
A<lmb4> a4;
C c;
// below doesn't compile: <lambda()>'{c} does not have 'constexpr' destructor
constexpr auto lmb5 = [c](){}; // C is not literal
A<lmb5> a5;https://stackoverflow.com/questions/62324050
复制相似问题