V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
justou
V2EX  ›  C++

请教一个 C++模板问题 (≥C++17)

  •  
  •   justou · 2022-04-16 11:40:16 +08:00 · 2377 次点击
    这是一个创建于 957 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有这样一些我无法修改的类(简化一下):

    struct SomeComplexBase{
        static inline int data_1 = 1;
        static inline int data_2 = 2;
        // ...
        // static inline int data_n = N;
    };
    
    struct ClassA: SomeComplexBase{
    
    };
    
    struct ClassB {
        typedef SomeComplexBase Base;
    };
    

    我自己实现的一个模板函数

    void func(){
    /*
     * 接收 ClassA, ClassB 类型
     * 有没有什么模板技巧可以消除 ClassA::data_n, ClassB::Base::data_n 这种不一致的成员访问方式?
     * 比如, 可以这样访问成员 NewType::data_n?
     *
     * if constexpr(std::is_same_v(T, ClassB))的方式纵然可行,
     * 但是两个分支代码都是一样的, 仅有以上提到的访问成员不一致的区别, 跟写两个完全重复的模板函数一样.
     * */
    }
    

    谢谢各位

    10 条回复    2022-04-16 21:49:46 +08:00
    ink19
        1
    ink19  
       2022-04-16 11:56:53 +08:00   ❤️ 2
    ```C++
    #include <iostream>

    struct SomeComplexBase{
    static inline int data_1 = 1;
    static inline int data_2 = 2;
    // ...
    // static inline int data_n = N;
    };

    struct ClassA: SomeComplexBase{

    };

    struct ClassB {
    typedef SomeComplexBase Base;
    };

    template<typename T, bool>
    struct DeWapper;

    template<typename T>
    struct DeWapper<T, false> {
    typedef typename T::Base Base;
    };

    template<typename T>
    struct DeWapper<T, true> {
    typedef T Base;
    };

    template<typename T>
    struct De {
    typedef typename DeWapper<T, std::is_base_of_v<SomeComplexBase, T>>::Base Base;
    };

    template<typename T>
    int func() {
    return De<T>::Base::data_1;
    }

    int main() {
    std::cout << func<ClassA>() << std::endl;
    std::cout << func<ClassB>() << std::endl;
    }
    ```
    yelite
        2
    yelite  
       2022-04-16 11:58:59 +08:00 via iPhone   ❤️ 1
    能不能对于每个 data_n 写一个 template 的 getter function ,然后对这两个类 specialize
    TuneG
        3
    TuneG  
       2022-04-16 11:59:01 +08:00 via iPhone   ❤️ 1
    这个,使用 typedef 就会有这种问题,如果不能改成 alias declaration ,那的确没有好方法,标准库中 type trait 也是有这种历史代码使用嵌套在模版化的 struct 里的 typedef 实现的,所以存在两套调用,对应的每种都有对应的别名模版调用方式,例如 std::remove_const<T>::type 的别名模版调用 std::remove_const_t<T>,为啥标准库不统一起来,估计也没什么好方法,不如不统一
    ink19
        4
    ink19  
       2022-04-16 12:01:28 +08:00   ❤️ 1
    @ink19 应该是这个意思吧?直接加一层封装就好了,感觉函数式编程就是这样,功能都能想办法实现,就是加封装的层数问题
    justou
        5
    justou  
    OP
       2022-04-16 12:14:18 +08:00
    @ink19 是这个意思, 我就知道肯定有办法的, 谢谢~
    GeruzoniAnsasu
        6
    GeruzoniAnsasu  
       2022-04-16 12:23:33 +08:00   ❤️ 1
    https://godbolt.org/z/vq6hM4534

    定义一个 trait 类,其中有一个类型成员 BaseType:
    当 T::Base 存在时 using BaseType= T::base
    当 T 继承自 SomeBase 时 using BaseType=SomeBase

    另外介绍一下这个站:
    https://cpppatterns.com/patterns/function-template-sfinae.html
    https://cpppatterns.com/patterns/class-template-sfinae.html


    我几乎每回重新写 trait 都要来查一下。。
    justou
        7
    justou  
    OP
       2022-04-16 12:28:15 +08:00
    @GeruzoniAnsasu 也是好办法, 谢谢推荐
    dangyuluo
        8
    dangyuluo  
       2022-04-16 12:56:13 +08:00
    虽然用 Traits 可以解决这个问题,但是从另一个角度想,func 似乎不应该同时接受 classA 和 classB ,毕竟一个是 IS-A, 一个是 HAS-A (借用继承的思路)
    lujunliang
        9
    lujunliang  
       2022-04-16 16:55:09 +08:00
    @GeruzoniAnsasu 请教一下,这里 constexpr _helper 的函数体在什么情况下允许为空的呢?
    GeruzoniAnsasu
        10
    GeruzoniAnsasu  
       2022-04-16 21:49:46 +08:00
    @lujunliang 模板只有在用到的时候才会实例化,实例化前被剔除掉的话根本不会进入语法检查阶段。 trait 类的所有成员最终只会留下 BaseType**推导结果** 的定义。在这个类里写一个 int a(){return "X";} 编译器都是不管的。所以只把声明留下来不要函数体也可以,反正最后编译器不会去检查这个函数的定义在哪里,只是用声明计算类型而已。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1074 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:26 · PVG 04:26 · LAX 12:26 · JFK 15:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.