我尝试使用Concepts Lite来指定一个概念来约束具有成员函数模板的更高级的kinded类型。但是,我无法在technical specification或tutorial内找到一个处理概念中模板化语句的子句。
这是怎么做到的?
示例:假设我的成员函数模板HKT
具有较高的kinded类型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
的替换是否失败,尽管我可以理解为什么这可能是一个问题:例如:应用约束以确保您的函数不会失败然后失败导致约束被满足但在实例化时成员函数模板中的替换失败(如果成员函数模板被约束会有帮助吗?)。
答案 0 :(得分:7)
长话短说,现在你(我?)必须提供具体的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.F
U
,因为InputIterator
就足够了。这可以用来优化编译时性能......
...可能会以令人惊讶的方式与SFINAE交互,因为AFAIK你可以拥有一个概念重载函数(例如InputIterator
),它接受所有输入迭代器,除了一个( SFINAE!为什么有人这样做?!),因此可以通过概念检查,但在实例化时间吹......悲伤。
答案 1 :(得分:2)
让我们从您的评论中考虑您想要的要求:
// 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
。
继续迭代这个想法,直到你真正削减所需的功能到最低限度。也许你甚至不需要原型可以破坏?写作原型很难,但在这种情况下,做正确的事情很重要。