【发布时间】:2020-06-15 14:52:52
【问题描述】:
鉴于以下奇怪的重复模板模式 (CRTP) 代码示例:
template<typename X>
struct Base {
X f() const { return X{}; }
};
template<template<typename> typename T>
struct Derived : T<Derived<T>>
{};
const Derived<Base> d0{};
const Derived<Base> d1 = d0.f();
我开始怀疑是否可以使用概念来限制可能的基类集。我的想法,基于this answer 假设使用requires B<T, Derived<T>>,其中B 定义如下:
#include <concepts>
// ...
template<template<typename> typename T, typename X>
concept B = requires (T<X> t)
{
{ t.f() } -> std::convertible_to<X>;
};
显然,我不能使用这种形式:
template<template<typename> typename T> requires B<T, Derived<T>>
struct Derived : T<Derived<T>>
{};
因为 require-clause 中的 Derived 尚未定义。这个:
template<template<typename> typename T>
struct Derived requires B<T, Derived<T>> : T<Derived<T>>
{};
还有这个:
template<template<typename> typename T>
struct Derived : T<Derived<T>> requires B<T, Derived<T>>
{};
也不要解决问题。
有没有办法克服这些困难并将概念与 CRTP 结合起来?
(我在 GCC 10.0.1 上进行了测试。)
【问题讨论】:
-
"考虑到奇怪的重复模板模式 (CRTP)" 这是一个奇怪的 CRTP 示例。 CRTP 中的派生类通常知道它们将使用哪些 CRTP 基类。如果这是出于组合原因(您打算以 CRTP 方式从
Derived继承),那么最终的 CRTP 基类需要使用 final 派生类类型,而不是中间类型。 -
目前还不清楚这有什么意义。如果
Derived是一个不知道 CRTP 基类在做什么的中间类,它应该接受任何东西。如果Derived知道 CRTP 基类在做什么......那么它要么知道它正在使用哪个类(模板),要么知道这个模板的接口是什么。该接口将是定义 CRTP 基类的概念,而不是派生类。 -
@NicolBolas 1. 也许我没有理解你的评论,但我不认为我的例子很奇怪——我什至看过可变参数 CRTP,它很好。 2. 在我的示例中,我不想从
Derived继承。 3.我想明确定义概念为类之间的抽象层。 -
"我想明确地将概念定义为类之间的抽象层。" 但是你没有类。你的
Derived需要一个模板,而不是一个类。并且您的代码正在使用定义的类型开始实例化该模板。您不能基于尚不存在的东西来限制模板。 -
我想我不明白你想在这里建立什么样的关系。 CRTP 通常用于通过插入可以访问派生类和/或派生类可以访问的成员来将行为注入派生类。您是否试图对正在增强的特定事物施加限制?
标签: c++ c++20 crtp c++-concepts