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

时间:2017-04-29 08:57:50

标签: c++ c++-concepts

在范围规范N4622中,Same概念被定义为采用TU两种类型,但有时仅在requires内使用像:

{ t } -> Same<T>;

启用第二种U扣除的规则是什么? (例如,来自概念规范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>();
}

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

1 个答案:

答案 0 :(得分:4)

什么是占位符?

快速回顾一下,确保我们都在同一页面上:占位符类型几乎是,但不是一个类型。它是一种可以有效推断的类型的替身。 (类似地,还有占位符非类型[resp。模板],它们是非类型的替身。为了避免发生切线,我只是在这里提及它们的存在并使用无所不包的占位符从现在开始的术语。)

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

// the specifiers are all placeholders for `int`
auto i = 42;
auto& lref = i;
auto&& rref = 0;
decltype(auto) = 2 * i;

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

// two placeholders for `int`
std::pair<auto, auto> p = std::make_pair(1, 4);

// one placeholder
std::pair<auto, int> q = p;

这就是它变得棘手的地方:概念本身可以用作占位符:

template<typename Int>
concept bool Integral = …;

// placeholder for `int` constrained by `Integral<int>`
Integral a = 0;

// same as above
Integral<> b = a;

template<typename Lhs, typename Rhs>
concept bool EqualityComparable = …;

// another placeholder for `int`
// this time constrained by `EqualityComparable<int, double>`
EqualityComparable<double> c = a;

仔细阅读二进制EqualityComparable示例。使概念作为占位符的原因是第一个概念参数具有特殊处理,并且在参数列表中将。角括号列表中出现的参数(如果有)与后续参数相对应。

需求中的占位符

让我们为具有size()的东西写一个概念。为了演示,我们期望这个size()操作的结果应该可以用作Incrementable变量(而不是像Integral概念那样合理的东西)。

template<typename Incr>
concept bool Incrementable = requires(Incr incr) {
    ++incr;
};

template<typename Cont>
concept bool Sizeable = requires(Cont const cont) {
    // requirement #1
    { cont.size() } -> Incrementable
};

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

template<Incrementable Incr>
void valid_for_incrementable(Incr incr);

template<typename Cont>
concept bool Sizeable = requires(Cont const cont) {
    cont.size();
    valid_for_incrementable(cont.size());
};

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

现在一起

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

template<typename T>
concept bool Demo = requires(T t) {
    { t } -> C<T>;
};

实际上意味着我们在C<T, T>表达式上引入了t约束。如果占位符为C<int>,则约束将为C<T, int>,依此类推。