C++概念占位符类型推导

Mir*_*aja 10 c++ c++-concepts

在范围符合规范N4622Same概念被定义为采取两种类型TU,但有时用作内requires只有一个这样的:

{ t } -> Same<T>;
Run Code Online (Sandbox Code Playgroud)

能够扣除第二种类型的规则是什么U?(例如来自Concepts spec N4630)

最简单的类似例子是:

template <class T, class U>
concept bool C = (sizeof(T) == sizeof(U)) && (sizeof(U) != 1);

template <class T>
concept bool D = requires(T t){
  // How is U deduced here? 
  {t} -> C<T>; 
};

template <class T>
  requires D<T>
void fn() {}

int main() {
  // Fails with: unable to deduce placeholder type 'C<char>' from 't'
  // That's expected.
  //fn<char>();

  // But how does this work?
  fn<int>();
}
Run Code Online (Sandbox Code Playgroud)

使用g ++ 8.0.0和-fconcepts测试样本.

Luc*_*ton 5

什么是占位符?

\n\n

快速回顾一下,以确保我们\xe2\x80\x99都在同一页面上:占位符类型几乎是一种类型,但不完全是一种类型。它\xe2\x80\x99 是将被有效推导的类型的替代。(类似地,也有占位符非类型 [resp. 模板],它们是非类型的替代品。为了避免偏离正题,我\xe2\x80\x99ll 只是在这里提到它们的存在并使用包罗万象的占位术语今后。)

\n\n

在概念之前,我们唯一的占位符是autodecltype(auto)说明符:

\n\n
// the specifiers are all placeholders for `int`\nauto i = 42;\nauto& lref = i;\nauto&& rref = 0;\ndecltype(auto) = 2 * i;\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过概念,我们可以以更复杂的方式使用占位符:

\n\n
// two placeholders for `int`\nstd::pair<auto, auto> p = std::make_pair(1, 4);\n\n// one placeholder\nstd::pair<auto, int> q = p;\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里\xe2\x80\x99s 变得棘手:概念本身可以用作占位符:

\n\n
template<typename Int>\nconcept bool Integral = \xe2\x80\xa6;\n\n// placeholder for `int` constrained by `Integral<int>`\nIntegral a = 0;\n\n// same as above\nIntegral<> b = a;\n\ntemplate<typename Lhs, typename Rhs>\nconcept bool EqualityComparable = \xe2\x80\xa6;\n\n// another placeholder for `int`\n// this time constrained by `EqualityComparable<int, double>`\nEqualityComparable<double> c = a;\n
Run Code Online (Sandbox Code Playgroud)\n\n

仔细阅读二进制EqualityComparable示例。使概念作为占位符变得棘手的是,第一个概念参数经过特殊处理,并且不会参数列表中提及。尖括号列表中出现的任何参数(如果有)都对应于后续参数。

\n\n

需求中的占位符

\n\n

让\xe2\x80\x99s 为具有size(). 为了演示,我们\xe2\x80\x99 将期望此size()操作的结果应该可用作Incrementable变量(而不是像Integral概念这样的有意义的东西)。

\n\n
template<typename Incr>\nconcept bool Incrementable = requires(Incr incr) {\n    ++incr;\n};\n\ntemplate<typename Cont>\nconcept bool Sizeable = requires(Cont const cont) {\n    // requirement #1\n    { cont.size() } -> Incrementable\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们的要求#1 是一种特殊类型的复合要求。也就是说,它是一个占位符出现在语法Trailing-return-type中的类型。效果就像我们写的一样:

\n\n
template<Incrementable Incr>\nvoid valid_for_incrementable(Incr incr);\n\ntemplate<typename Cont>\nconcept bool Sizeable = requires(Cont const cont) {\n    cont.size();\n    valid_for_incrementable(cont.size());\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

简单地说,复合需求的目的是同时引入两个约束:括号中的表达式是有效的,并且它可以用作发明的约束验证函数模板的参数。

\n\n

现在都在一起了

\n\n

凭借我们对占位符及其在复合需求中的用途的了解,我们可以找到答案:

\n\n
template<typename T>\nconcept bool Demo = requires(T t) {\n    { t } -> C<T>;\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

有效意味着我们C<T, T>t表达式引入了约束。如果占位符是C<int>相反的,那么约束也会是C<T, int>相反的,依此类推。

\n