SFINAE用于功能体

tom*_*tom 3 c++ templates c++20

如果函数体没有意义(即不编译),我经常使用SFINAE从重载集中删除函数.是否可以向C++添加一个简单的require声明?

例如,让我们有一个功能:

template <typename T>
T twice(T t) {
  return 2 * t;
}
Run Code Online (Sandbox Code Playgroud)

然后我得到:

twice(1.0);
twice("hello");  // Error: invalid operands of types ‘int’ and ‘const char*’ to binary ‘operator*’
Run Code Online (Sandbox Code Playgroud)

我想得到一个错误,说明没有twice类型参数的函数const char *

我想写点类似的东西:

template <typename T>
requires function_body_compiles
T twice(T t) {
  return 2 * t;
}
Run Code Online (Sandbox Code Playgroud)

然后我会得到

twice(1.0);
twice("hello");  // Error: no matching function for call to ‘twice2(const char [6])’
Run Code Online (Sandbox Code Playgroud)

更多的动力:我正在观看关于琐碎类的移动语义的梦魇和他的最终SFINAE基本上说:在编译时使用这个构造函数.对于更复杂的构造函数来说,编写正确的SFINAE将是一场噩梦.

你认为添加requires function_body_compiles到c ++会有意义吗?还是我缺少一个根本问题?这会被滥用或滥用的严重程度如何?

Yak*_*ont 8

我们没有这个功能的最大原因是它很难.

这很难,因为它要求编译器能够编译几乎任意的C++代码,获取错误,然后干净利落地退出.

现有的C++编译器并非都是为此而设计的.事实上,MSVC花了十年时间才能合理地符合decltypeSFINAE的支持.

对全功能机构这样做会更加困难.


现在,即使很容易,也有理由不这样做.它以非常可怕的方式混合了实现和界面.

而不是这样,C++委员会正朝着完全不同的方向前进.

概念是您可以通过合理的,通常命名的方式表达对类型的要求的想法.他们来的是.

另一个答案提到,

template <typename T> requires requires(T t) { { 2 * t } -> T; }
T twice(T t) {
  return 2 * t;
}
Run Code Online (Sandbox Code Playgroud)

是一种方法,但这种方式被认为是不好的形式.相反,你应该写一个概念"可以乘以一个整数并返回相同的类型".

template<typename T>
concept IntegerScalable = requires(T t) {
  { 2 * t } -> T;
};
Run Code Online (Sandbox Code Playgroud)

我们可以

template <IntegerScalable T>
T twice(T t) {
  return 2 * t;
}
Run Code Online (Sandbox Code Playgroud)

我们完成了.

期望的下一步称为"检查概念".在已检查的概念中,它将概念转换为您的类型的一组编译时接口T.

然后检查函数体,以确保T不对任何不是概念要求的类型做任何事情.

使用理论上未来检查的概念,

template <IntegerScalable T>
T twice(T t) {
  T n = 7;
  if (n > t) return n;
  return 2 * t;
}
Run Code Online (Sandbox Code Playgroud)

即使在完成对模板的调用之前编译模板时,编译器也会拒绝这一点,因为这个概念IntegerScalable并不能保证你可以T用整数初始化a ,也不能用它T来比较>.另外我认为以上需要移动构造.


你今天可以做一个黑客攻击.

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }
Run Code Online (Sandbox Code Playgroud)

然后你的代码可以写成:

template<class T>
T twice(T t)
RETURNS( 2 * t )
Run Code Online (Sandbox Code Playgroud)

你将得到一个SFINAE友好版本twice.它也将尽可能不受欢迎.

@Barry提出了一种=>用于替换RETURNS和其他东西的变体,但是自从我看到它移动已经过去了一年.

同时,RETURNS做了大部分繁重的工作.