我想知道编译器如何对待lambda函数而不是常规函数。我认为它甚至不包括捕获列表,它的行为似乎也略有不同。
例如,在上一篇文章Trying to pass a constexpr lambda and use it to explicitly specify returning type中,我使用了constexpr lambda并将其作为常规函数参数传递。我引用了部分答案。
constexpr函数的参数本身不是constexpr对象-因此您不能在常量表达式中使用它们。
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
return std::array<event, (l())>{};
}
// Compiles with GCC (C++17), though ill-formed (according to the answer of my last post)
尽管,引起我注意的是,这确实通过constexpr lambda
进行编译,但是没有通过constexpr常规(全局)函数进行编译。是什么原因导致这种差异?
常规函数和lambda函数之间的编译器行为是否还有其他区别?
编辑:Lambda实现示例
foo([](){ return 4; }); // C++17 Lambda's are implicitly constexpr
在这种情况下,lambda基本上是该值的包装器。
编辑:全局功能
每当传递全局函数时,与使用lambda相对,编译器都会抱怨该函数(无论是否定义为constexpr)都不能在恒定条件下使用:
prog.cc:8:20: error: 'l' is not a constant expression
答案 0 :(得分:1)
从您的代码段中删除一些无关的内容,我们得到了
template<typename T>
constexpr void foo(T t)
{
constexpr int i = t();
}
constexpr int f() { return 42; }
auto l = []{ return 42; }
备注:
您正在尝试将t()
内的constexpr
用作foo
。 foo
实际上可以是一个正常函数,并且仍然具有相同的功能。
lambda 不是一个函数。这是带有operator()
的匿名结构。
struct L { constexpr int operator()() const { return 42; } };
T
被推导为与呼叫L
中的foo(l)
等效的类型。
T
在呼叫int(*)()
中推导为foo(f)
。
在两种情况下,t
不是 foo
中的constexpr。
表达式
e
是一个核心常量表达式,除非根据抽象机的规则对e
的求值将对以下表达式之一求值:[...]
从左值到右值的转换,除非[...]
一个非易失性glvalue,它引用用constexpr定义的非易失性对象,或者引用该对象的一个非可变子对象,或者
文字类型的非易失性glvalue,指的是其生命周期始于
e
的非易失性对象;
通过int(*)()
进行调用需要从左值到右值的转换。 t
不是用constexpr
定义的对象,它也没有在t()
的评估中开始其生命周期。因此,t()
不是constexpr
中的foo(f)
。
调用operator()
不需要从左值到右值的转换。由于没有成员访问权限,因此它只是一个constexpr
函数调用。因此,t()
是constexpr
中的foo(l)
。
从核心常量表达式到常量表达式都有one more step,但是对于此处的讨论并不重要。