如何最好地实现基于模板参数包专用化的带成员的派生?

How do I best implement a Derived with members depending on a Template Parameter Pack Specialization?

提问人:kaisong 提问时间:10/27/2023 更新时间:10/27/2023 访问量:39

问:

目标

我想实现一个结构,满足某些要求。我有一个有效的解决方案代码,但它有某些缺点。我寻求更好的方法。Derived<size_t ...Tpar>

要求

Derived<size_t ...Tpar>必须以独占方式继承自 。 对我来说,相关案例是 和 .Base<size_t ...Tpar>Derived<size_t>Derived<>

成员和属性:

  • 空 Tpar 和非空 Tpar 的相同成员函数void a(void);
  • 仅用于非空 Tpar 的长度数组dataTpar[0]Derived<Tpar...>
  • 以及一个仅用于非空 Tpar 的函数,该函数在 上执行计算。void b(void);data

最小工作示例

正如以下示例代码中的注释所示,这些问题使解决方案没有吸引力:

  1. 既然和两者都实现了,我就想到了继承。但这不起作用,因为会继承自 ,这违反了要求。Derived<>Derived<size_t>a()Derived<size_t>Derived<>Derived<size_t>Base<>
  2. 因此,我选择了一个专门的子模块。但这不起作用,因为 Derived 已经是一个模板,因此我无法根据语言规则在其命名空间中专门化子模块模板。
  3. 因此,我将子模块制作为两个非模板显式变体,并用于在任一专业化中获得正确类型的子模块。 3.1. 数组有没有更好的替代方案(我必须用零来增加,只是为了条件不抱怨是空指针? 3.2. 有没有办法在不将子模块暴露给编译单元的情况下对子模块进行模板化或使整个代码更短?conditional_tDerivedaTpar[0]
#include<iostream>
#include<array>
#include<type_traits>

template<size_t ...Tpar> class Base{};

// I cannot inherit Derived<Tpar> from Derived<> since this would make Derived<Tpar> a derived of Base<>.
template<size_t ...Tpar>
class Derived: Base<Tpar...>{
    // c++ won't allow template structure specializations within a template struct
    template<size_t n>
    struct SubModule_paged{
        double data[n];
        void foo(){
            // foo is a routine that computes on data.
            std::cout << "Sub<"<<n<<">foo.\n";
        }
    };
    struct SubModule_empty{
        // no data, hence no foo.
        void foo() = delete; // COMPILER HINT: Intentionally, Derived<> has no member b.
    };
    static constexpr size_t nTpar = sizeof...(Tpar);
    static constexpr std::array<const size_t, nTpar+1> aTpar = {Tpar...,0}; // <-- This is ugly!
    using SubModuleType = std::conditional_t< (nTpar>0) , SubModule_paged<aTpar[0]> , SubModule_empty >;
    //
    SubModuleType sub;
    //
public:
    void a(){
        std::cout << "a.\n";
    }
    void b(){
        std::cout << "b.\n";
        sub.foo();
    }
};

int main(){
    Derived< > x;
    Derived<3> y;
    
    x.a();
    //x.b();
    
    y.a();
    y.b();
}
C++ 继承 模板专用化 参数包

评论


答:

3赞 463035818_is_not_an_ai 10/27/2023 #1

空和非空的相同成员函数void a(void);Tpar

好的,应该是 的成员。aBase

长度为 for 的数组数据,仅适用于非空 以及仅用于非空的函数 void,用于对数据执行计算。Tpar[0]Derived<Tpar...>Tparb(void);Tpar

好的,专门研究何时至少有一个参数。DerivedTpar...

#include <iostream>
#include <array>

template<size_t ...Tpar> struct Base{
    void a(void) {}
};

// base template
template <size_t ...Tpar> struct Derived : public Base<Tpar...> {};

// specialization for one or more size_t 
template <size_t Tfirst,size_t ...Tpar> struct Derived<Tfirst,Tpar...> : public Base<Tfirst,Tpar...> {
    std::array<size_t,Tfirst> arr;
    void b() {}
};

int main() {
    Derived<> d;
    //d.b(); error
    //d.arr; error
    Derived<42> e;
    e.b();
    e.arr;
}

也许你不会同意成为 的成员,但是在空和非空之间区分的事情实际上相当简单。你只需要考虑这是一个或多个参数,而是零或更多。我想你对把参数包想象成一个数组感到困惑,你需要通过 .但是参数包不是数组,也不需要数组来选取第一个元素。aBaseTpar...size_t Tfirst,size_t ...Tparsize_t Tpar...Tpar[0]

评论

0赞 kaisong 10/27/2023
谢谢。我了解到这是可能的,并且仍然专业化,而没有这意味着 Base 的两个专业化是继承的。我不确定是否确实使 a() 成为 base 的成员。可以在中间放置一个层来删除,这样它就不会在所有其他继承自 base 的类中闪耀。有没有比底层更好的东西?template <size_t ...Tpar> struct Derived : public Base<Tpar...> {};
0赞 463035818_is_not_an_ai 10/27/2023
@Kaisong你的意思是还有其他类型继承而不需要?Basea
0赞 kaisong 10/27/2023
@Aconcagua,如果 a() 不是多个复杂函数,我会同意。可能最好的办法是一直从实现 a() 的第二个基继承所有派生。---基本上这就是你通过基地提出的答案。所以我把它标记为解决方案。
0赞 463035818_is_not_an_ai 10/27/2023
我感觉到你没有向我们解释的复杂性。请注意,设计不是凭空出现的,而是从具体的需求中产生的。当你不说出它的意思时,“更好”并不意味着什么。我只能考虑你在问题中提到的三颗子弹。