65

我正在尝试指定一个概念来约束具有使用 Concepts Lite 的成员函数模板的更高种类的类型。但是,我无法在技术规范教程中找到处理概念中模板化语句的子句。

这是怎么做到的?

HKT示例:假设我有一个带有成员函数模板的更高种类的类型F

template<class T>
struct HKT {
  template<class U> // this looks like e.g. rebind in std::allocators
  auto F(U) -> HKT<U>;
};

现在我想指定一个概念来约束这些更高种类的类型:

template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) { // HKT<T> is a type, h is an object
    // HKT<T> needs to have a member function template that 
    // returns HTK<U> where the type U is to be deduced and
    // it can be any type (it is unconstrained)
    template<class U>  // is there a syntax for this?
    h.F(std::declval<U>()) -> HKT<U>; 
  }
}

请注意,我可以执行以下操作:

template <template <class> class HKT, class T, class U>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) {
      h.F(std::declval<U>()) -> HKT<U>;
  }
}

但这意味着我需要U在约束站点知道。

我真的不在乎替换给定值是否U失败,尽管我知道为什么这可能是一个问题:例如应用约束以确保您的函数不会失败然后失败导致约束得到满足但在实例化时成员函数模板中的替换失败(如果成员函数模板受到约束,会有帮助吗?)。

4

2 回答 2

9

长话短说,现在你(我?)必须提供一个具体的U

template <template <class> class HKT, class T, class U = T>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) { // HKT<T> is a type, h is an object
    h.F(std::declval<U>()) -> HKT<U>; 
  }
}

因为编译器不能证明所有U可能存在的类型成员函数模板都可以工作,也就是说,以下是没有希望的:

template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) {
    template<class U>  // for all those Us that haven't been written yet...
    h.F(std::declval<U>()) -> HKT<U>; 
  }
}

在一个假设的 5 分钟内设计的概念精简版实现中,我们能够限制U 一点点

template <template <class> class HKT, class T, 
          InputIterator U = InputIterator()  /* imaginary syntax */ >
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) {
      h.F(std::declval<U>()) -> HKT<U>; // Is InputIterator enough to instantiate F?
  }
}

编译器只需要检查一个模型InputIterator是否足以实例化h.F,即使h.F没有约束也是可能的!此外,U只提供它模型的检查,InputIterator甚至不需要尝试检查h.FU因为InputIterator就足够了。这可用于优化编译时性能和...

...可能会以令人惊讶的方式与 SFINAE 交互,因为 AFAIK 您可以拥有一个概念重载函数(例如 for InputIterator),它接受除那个之外的所有输入迭代器(SFINAE!为什么有人会这样做?!),因此可以通过概念检查但在实例化时吹了......伤心。

于 2014-12-17T09:45:39.073 回答
4

让我们从您的评论中考虑您想要的要求:

// HKT<T> needs to have a member function template that 
// returns HTK<U> where the type U is to be deduced and
// it can be any type (it is unconstrained)

虽然 Concepts 要求我们将约束基于具体类型,但我们可以明智地选择我们使用的具体类型。你的意思U任何类型。真的是任何类型,无论如何?考虑一下您可能拥有的最小约束集,U让我们构建一个满足它们的类型。这被称为.U

我首先想到的“任何类型”实际上是半规则类型。一种默认可构造、可复制和可分配的类型。所有正常的好东西:

namespace archetypes {
    // private, only used for concept definitions, never in real code
    struct Semiregular { };
}

archetypes::Semiregular是一个具体的类型,所以我们可以用它来构建一个概念:

template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF = 
  requires(HKT<T> h, archetypes::Semiregular r) {
    {h.F(r)} -> HKT<archetypes::Semiregular>
  };

archetypes::Semiregular是私有类型。它不应该被 知道HKT,因此如果h.F(r)它格式正确并返回可转换为 的类型HKT<archetypes::Semiregular>,则几乎可以肯定它是一个成员函数模板。

那么问题是,这是一个好的原型吗?我们需要U是半规则的,还是不规则的类型也可以?您需要的操作越少,您的原型中应该存在的操作就越少。也许您所需要的U只是可移动的:

namespace archetypes {
    // private, only used for concept definitions, never in real code
    struct Semiregular { };

    struct Moveable {
        Moveable() = delete;
        Moveable(Moveable&& ) noexcept(false);
        Moveable(Moveable const& ) = delete;
        ~Moveable() = default;

        Moveable& operator=(Moveable const& ) = delete;
        Moveable& operator=(Moveable&& ) noexcept(false);
    };
}

template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF =
  requires(HKT<T> h, archetypes::Moveable m) {
    { h.F(m) } -> HKT<archetypes::Moveable>
  };

我们正在测试相同的想法 -F()使用不知名的类型进行调用,并且返回类型除外以反映这一点,因此要求它是一个函数模板。但是现在我们为该类型提供了更少的功能。如果F()适用于任何,它将适用于archetypes::Moveable

继续迭代这个想法,直到您真正将所需的功能减少到最低限度。也许你甚至不需要原型是可破坏的?编写原型很难,但在这种情况下,正确处理很重要。

于 2017-04-18T17:38:37.720 回答