c++ - SFINAE 自动检查函数体是否在没有显式约束的情况下编译
问题描述
如果函数体没有意义(即无法编译),我经常使用 SFINAE 从重载集中删除函数。是否可以在 C++ 中添加一个简单的require
语句?
例如,让我们有一个函数:
template <typename T>
T twice(T t) {
return 2 * t;
}
然后我得到:
twice(1.0);
twice("hello"); // Error: invalid operands of types ‘int’ and ‘const char*’ to binary ‘operator*’
我想得到一个错误,说没有twice
类型参数的函数const char *
我很想写一些类似的东西:
template <typename T>
requires function_body_compiles
T twice(T t) {
return 2 * t;
}
然后我会得到
twice(1.0);
twice("hello"); // Error: no matching function for call to ‘twice2(const char [6])’
更多动力:我正在观看The Nightmare of Move Semantics for Trivial Classes的演讲,他最后的 SFINAE 基本上是在说:在编译时使用这个构造函数。对于更复杂的构造函数,编写正确的 SFINAE 将是一场噩梦。
你认为添加requires function_body_compiles
到 c++ 中有意义吗?还是我缺少一个基本问题?这会被滥用或误用有多严重?
解决方案
我们没有这个功能的最大原因是它很难。
这很难,因为它要求编译器能够编译几乎任意的 C++ 代码,得到错误,然后干净地退出。
现有的 C++ 编译器并非全部设计用于执行此操作。事实上,MSVC 花了十年的大部分时间才获得合理合规的decltype
SFINAE 支持。
为全功能机构这样做会更加困难。
现在,即使这很容易,也有理由不这样做。它以一种非常可怕的方式混合了实现和接口。
C++ 委员会并没有走这条路,而是朝着完全不同的方向前进。
概念是您可以以合理的、通常命名的方式表达对类型的要求的想法。他们将出现在c++20中。
正如另一个答案提到的,
template <typename T> requires requires(T t) { { 2 * t } -> T; }
T twice(T t) {
return 2 * t;
}
是一种方法,但这种方法被认为是不好的形式。相反,你应该写一个概念“可以乘以一个整数并得到相同的类型”。
template<typename T>
concept IntegerScalable = requires(T t) {
{ 2 * t } -> T;
};
然后我们可以
template <IntegerScalable T>
T twice(T t) {
return 2 * t;
}
我们完成了。
所需的下一步称为“检查概念”。在检查的概念中,它转换为您的类型的一组编译时接口的概念T
。
然后检查函数的主体以确保不对任何T
不是概念要求的类型进行任何操作。
使用理论上的未来检查概念,
template <IntegerScalable T>
T twice(T t) {
T n = 7;
if (n > t) return n;
return 2 * t;
}
即使在调用模板完成之前编译模板时,编译器也会拒绝这一点,因为这个概念IntegerScalable
不能保证您可以T
用整数初始化 a ,也不能保证您可以T
用>
. 另外,我认为以上需要移动构造。
您今天可以做一个 hack。
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
那么你的代码可以写成:
template<class T>
auto twice(T t)
RETURNS( 2 * t )
您将获得 SFINAE 友好版本的twice
. 它也将尽其所能。
@Barry提出了这种=>
用于替换RETURNS
和其他一些东西的变体,但是自从我看到它移动以来已经有一年了。
同时,RETURNS
承担大部分繁重的工作。
推荐阅读
- c - 奇怪的 C 分段错误
- javascript - 激活路由后如何缓存组件
- node.js - 来自 mongo db 的节点输出作为对象列表
- javascript - 如何将索引从 *ngFor 附加到 attr.id - HTML
- swift - 在 Xcode 中为 LLDB 调试器添加快捷方式以进行 Swift 调试
- android - 在浮动操作按钮中删除图像资源的透明
- php - 根据支付网关和转换状态更改 Woocommerce 订单状态
- php - 如何在 php codeigniter 中使用 sftp 解决多上传问题
- javascript - 在电子的vue组件中打印画布
- typescript - 打字稿创建带有错误的类型保护