未评估上下文中的默认模板参数和 lambda:错误还是功能?
Default template parameter & lambda in unevaluated context: bug or feature?
我们考虑使用完全相同的语法创建两种不同类型的目标。这可以通过 lambda 轻松完成:
auto x = []{};
auto y = []{};
static_assert(!std::is_same_v<decltype(x), decltype(y)>);
但是,我们正在寻找另一种更优雅的语法,而不是使用 lambda。以下是一些测试。我们首先定义一些工具:
#include <iostream>
#include <type_traits>
#define macro object<decltype([]{})>
#define singleton object<decltype([]{})>
constexpr auto function() noexcept
{
return []{};
}
template <class T = decltype([]{})>
constexpr auto defaulted(T arg = {}) noexcept
{
return arg;
}
template <class T = decltype([]{})>
struct object
{
constexpr object() noexcept {}
};
template <class T>
struct ctad
{
template <class... Args>
constexpr ctad(const Args&...) noexcept {}
};
template <class... Args>
ctad(const Args&...) -> ctad<decltype([]{})>;
以及以下变量:
// Lambdas
constexpr auto x0 = []{};
constexpr auto y0 = []{};
constexpr bool ok0 = !std::is_same_v<decltype(x0), decltype(y0)>;
// Function
constexpr auto x1 = function();
constexpr auto y1 = function();
constexpr bool ok1 = !std::is_same_v<decltype(x1), decltype(y1)>;
// Defaulted
constexpr auto x2 = defaulted();
constexpr auto y2 = defaulted();
constexpr bool ok2 = !std::is_same_v<decltype(x2), decltype(y2)>;
// Object
constexpr auto x3 = object();
constexpr auto y3 = object();
constexpr bool ok3 = !std::is_same_v<decltype(x3), decltype(y3)>;
// Ctad
constexpr auto x4 = ctad();
constexpr auto y4 = ctad();
constexpr bool ok4 = !std::is_same_v<decltype(x4), decltype(y4)>;
// Macro
constexpr auto x5 = macro();
constexpr auto y5 = macro();
constexpr bool ok5 = !std::is_same_v<decltype(x5), decltype(y5)>;
// Singleton
constexpr singleton x6;
constexpr singleton y6;
constexpr bool ok6 = !std::is_same_v<decltype(x6), decltype(y6)>;
和以下测试:
int main(int argc, char* argv[])
{
// Assertions
static_assert(ok0); // lambdas
//static_assert(ok1); // function
static_assert(ok2); // defaulted function
static_assert(ok3); // defaulted class
//static_assert(ok4); // CTAD
static_assert(ok5); // macro
static_assert(ok6); // singleton (macro also)
// Display
std::cout << ok1 << std::endl;
std::cout << ok2 << std::endl;
std::cout << ok3 << std::endl;
std::cout << ok4 << std::endl;
std::cout << ok5 << std::endl;
std::cout << ok6 << std::endl;
// Return
return 0;
}
这是使用GCC的当前主干版本编译的,选项-std=c++2a
。在编译器资源管理器中查看此处的结果。
事实上,ok0
、ok5
和ok6
工作并不令人惊讶。然而,ok2
和ok3
true
而ok4
却不是,这一事实对我来说非常令人惊讶。
- 有人可以解释一下使
ok3
true
但ok4
false
的规则吗? - 它真的应该如何工作,还是这是一个关于实验性功能(未评估上下文中的 lambda)的编译器错误?(非常欢迎参考标准或C++建议)
注意:我真的希望这是一个功能而不是一个错误,而只是因为它使一些疯狂的想法可以实现
有人可以解释使 ok3 为真的规则吗 但是OK4是假的?
ok3 为 true,因为使用 lambda 类型作为默认类型。
lambda 表达式的类型(也是闭包的类型) object)是唯一的、未命名的非联合类类型,
因此,object
的默认模板类型、macro
的模板参数类型以及singltone
每次初始化后总是不同的。但是,对于函数function
调用返回的 lambda 是唯一的,并且其类型是唯一的。模板函数ctad
只有参数的模板,但返回值是唯一的。如果重写函数为:
template <class... Args, class T = decltype([]{})>
ctad(const Args&...) -> ctad<T>;
在这种情况下,返回类型将在每次实例化后延迟。
对于 ok2 函数参数类型 (T) 取决于指定的模板参数。 对于 OK3 CTOR 不是模板。
对于 OK4,两个扣除都依赖于相同的参数类型列表(在本例中为空),并且由于该扣除仅发生一次。 模板实例化和推导是不同的东西。 对于相同的参数类型,列表推导仅发生一次,而实例化则针对所有用法发生。
查看此代码 (https://godbolt.org/z/ph1Wk2)。 如果扣除的参数不同,则进行单独的扣除。
- 尝试将lambda函数放在队列中时出现一般分配器错误(可能是与unique_ptr有关的错误)
- VS Code C++:不准确的系统包括路径错误(wchar.h,boost/lambda/lambda.hpp)
- 错误:无法将"<lambda(double)>"转换为"double(*)(double)"
- Tbb 库:错误:编写自定义类函数而不是 lambda 表达式时,对函数的调用不匹配
- 通过实用程序 fn 将捕获的 lambda 传递给 C 样式回调 - 错误
- std::映射服装比较函数和函数/lambda错误
- 为什么访问我的引用捕获变量会导致我的 lambda 函数出现段错误?
- 从两个 lambda 的函数返回 lambda 时的链接器错误
- 为什么Qt在信号和插槽中为lambda表达式抛出错误?
- 处于默认参数位置的 Lambda 无法访问好友成员。这是编译器错误吗?
- 组合 lambda 捕获时出现编译错误
- 将 lambda 存储为 std::函数时出现分段错误
- 尝试使用具有尾随返回类型的 lambda 进行 SFINAE 时出现硬错误
- "this" Lambda 捕获的是不正确的。海湾合作委员会编译器错误?
- 使用本地类型声明的G lambda被使用但从未定义 - 确实是一个错误
- lambda deleter for Unique_ptr导致编译错误与函数
- 为什么使用递归lambda时会遇到编译错误
- "如果 constexpr",在 lambda 内部,在包扩展内部 - 编译器错误?
- Gtk+ g_signal_connect() 和 C++ lambda 会导致"invalid cast"错误
- 如果我的模板类型首先以lambda参数而发生,则MSVC会引起一个奇怪的错误