"template T is a..."形式的条件如何用概念表达?
How to express conditions of the form "template T is a..." using concepts?
很容易表达"type T is a container of E":
template <class T, class E>
concept bool is_container = requires(T t, E e) {
{ t.push_back(e) } -> void;
};
template <class T, class E> requires is_container<T,E>
void moo()
{
T t; E e;
t.push_back(e);
}
(具体检查什么并不重要)
现在我需要这样表达一个条件:"for any type E, T is a container".
template <template<class> class T> requires is_container<T>
void moo()
{
T<int> ti;
ti.push_back(1);
T<std::string> ts;
ts.push_back("abc"s);
}
这种情况下is_container
怎么写?
基本上,您所要求的内容不可能按照您暗示的方式实现,但通过简单的解决方法,您可以获得相同的结果。
要知道 T<E>
是任何类型 E
的容器,编译器需要为每个可能的 E
实例化 T<E>
并检查条件,这是不可能的,因为有无限多种可能的类型T<E>
(想象一下链T<T<E>>
)。
您可以做的是从公共基础 class(标签)派生所有容器 classes(那些是任何 E
的容器)。
struct container_tag { };
template<class E>
class existing_container_class : public container_tag
{
public:
using value_type = E;
/* ... */
};
然后写一个这样的概念:
template<class T>
concept bool concept_container = std::is_base_of_v<container_tag, T>;
并像这样使用它:
template<concept_container T>
void moo() {
T t;
T::value_type e;
t.push_back(e);
}
像c++中的许多不可能的事情一样,这相当于解决了Halt。
您有一个图灵完备语言的任意函数(您的模板),并希望确定您的函数的每个输出(模板的每个实例)是否满足某些 属性。
莱斯定理说这是不可能的。
这是像模板这样的图灵完备元编程语言的缺点之一。
现在您可以随身携带模板并根据特定类型进行检查。但是在一个足够复杂的程序中,这很像发明用来解决问题的概念;接口错误而不是深度实现错误。
缺少 C++ 模板 parametricity。给定一个模板,只能检查其特定实例的属性,而不能检查模板本身(或 "all its instantiations",可以说是)。不同的实例化可能有不同的属性。
所以显然不可能写出像is_container
的第二种变体这样的概念。
这是一个非常简单、老套的部分解决方法。这个想法是,如果一个模板实例化并适用于某种随机类型,它也可能适用于其他类型。当然,不受控制的专业化和高级模板魔术会打破这个假设,但我们只是回到了常规未经检查的模板的原点。
template <class T, class E>
concept bool is_container = requires(T t, E e) {
{ t.push_back(e) } -> void;
};
struct random {};
template <template<class> class T>
concept bool is_generic_container = is_container<T<random>, random>;
很容易表达"type T is a container of E":
template <class T, class E>
concept bool is_container = requires(T t, E e) {
{ t.push_back(e) } -> void;
};
template <class T, class E> requires is_container<T,E>
void moo()
{
T t; E e;
t.push_back(e);
}
(具体检查什么并不重要)
现在我需要这样表达一个条件:"for any type E, T is a container".
template <template<class> class T> requires is_container<T>
void moo()
{
T<int> ti;
ti.push_back(1);
T<std::string> ts;
ts.push_back("abc"s);
}
这种情况下is_container
怎么写?
基本上,您所要求的内容不可能按照您暗示的方式实现,但通过简单的解决方法,您可以获得相同的结果。
要知道 T<E>
是任何类型 E
的容器,编译器需要为每个可能的 E
实例化 T<E>
并检查条件,这是不可能的,因为有无限多种可能的类型T<E>
(想象一下链T<T<E>>
)。
您可以做的是从公共基础 class(标签)派生所有容器 classes(那些是任何 E
的容器)。
struct container_tag { };
template<class E>
class existing_container_class : public container_tag
{
public:
using value_type = E;
/* ... */
};
然后写一个这样的概念:
template<class T>
concept bool concept_container = std::is_base_of_v<container_tag, T>;
并像这样使用它:
template<concept_container T>
void moo() {
T t;
T::value_type e;
t.push_back(e);
}
像c++中的许多不可能的事情一样,这相当于解决了Halt。
您有一个图灵完备语言的任意函数(您的模板),并希望确定您的函数的每个输出(模板的每个实例)是否满足某些 属性。
莱斯定理说这是不可能的。
这是像模板这样的图灵完备元编程语言的缺点之一。
现在您可以随身携带模板并根据特定类型进行检查。但是在一个足够复杂的程序中,这很像发明用来解决问题的概念;接口错误而不是深度实现错误。
缺少 C++ 模板 parametricity。给定一个模板,只能检查其特定实例的属性,而不能检查模板本身(或 "all its instantiations",可以说是)。不同的实例化可能有不同的属性。
所以显然不可能写出像is_container
的第二种变体这样的概念。
这是一个非常简单、老套的部分解决方法。这个想法是,如果一个模板实例化并适用于某种随机类型,它也可能适用于其他类型。当然,不受控制的专业化和高级模板魔术会打破这个假设,但我们只是回到了常规未经检查的模板的原点。
template <class T, class E>
concept bool is_container = requires(T t, E e) {
{ t.push_back(e) } -> void;
};
struct random {};
template <template<class> class T>
concept bool is_generic_container = is_container<T<random>, random>;