关于 C++ 模板一个问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
iqoo
V2EX    程序员

关于 C++ 模板一个问题

  •  
  •   iqoo 2023 年 8 月 11 日 2180 次点击
    这是一个创建于 889 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如下代码,log 函数接受 Base 类及其子类 Str 、Num 。调用时无需手动指定类型,传入 char* 可自动生成 Str 类,传入 int 可自动生成 Num 类。

    struct Base { void print() { } }; struct Str : Base { Str(const char* s) {} }; struct Num : Base { Num(int v) {} }; template<typename T> void log(T obj) { obj.print(); } int main() { log("hello"); log(123); } 

    如何让模板根据传入的类型推算出用什么类?例如传入 char* 时选择 Str 类,传入 int 时选择 Num 类。

    第 1 条附言    2023 年 8 月 11 日
    感谢回答。

    如果能直接指定模板参数的类型集合就好了,例如 Str | Num ,编译器按照依次尝试去构造,这样写起来会方便些。上述只是个伪代码,实际有多个类,如果每种类的构造参数都自己拿出来写一遍,还是有些累赘。

    不知用 c++ concept 约束模块类型是否有效?(编译器使用 C++23 )

    如果 log 函数只有一个参数用 overload 更方便,几种类型写一遍就可以了。实际的 log 函数是可变参数,每个参数都可以是 Str | Num | Base 。
    12 条回复    2023-08-11 22:40:25 +08:00
    NessajCN
        1
    NessajCN  
       2023 年 8 月 11 日
    void log(T param)
    {
    if constexpr (std::is_same_v<T, const char*>)
    Str obj(param);
    obj.print();
    else if constexpr (std::is_same_v<T, int>)
    Num obj(param);
    obj.print();
    else
    std::cout << "???" << std::endl;
    }
    geelaw
        2
    geelaw  
       2023 年 8 月 11 日   2
    楼主的代码明明是 log 可以接受任何可以 .print 的类型。提的需求也非常不明确。

    你希望模板自动推断存在可以构造的 Base 的子类 T ,还是希望 T 就是参数本身的类型,然后 obj.print 改写为 T1{obj}.print(),其中 T1 是 Base 的某个子类并且可以被 obj 所构造?

    另外,建议想清楚自己的问题,如果不是适合用模板解决的问题的话,会从一个小问题,变成一个报错非常长的问题。
    aglargilwangz
        3
    aglargilwangz  
       2023 年 8 月 11 日   3
    ```cpp
    #include <iostream>

    struct Base {
    void print() {
    std::cout << "Base class\n";
    }
    };

    struct Str : Base {
    Str(const char* s) {}
    void print() {
    std::cout << "String class\n";
    }
    };

    struct Num : Base {
    Num(int v) {}
    void print() {
    std::cout << "Number class\n";
    }
    };

    template<typename T>
    struct Mapper;

    template<>
    struct Mapper<const char*> {
    using type = Str;
    };

    template<>
    struct Mapper<int> {
    using type = Num;
    };

    template<typename T>
    void log(T obj) {
    typename Mapper<T>::type mappedObj(obj);
    mappedObj.print();
    }

    int main() {
    log("hello");
    log(123);
    }
    ```
    hhjuteman
        4
    hhjuteman  
       2023 年 8 月 11 日
    你给的代码编译不了,我用的楼上的。
    搞不懂的多用 godbolt
    https://godbolt.org/z/cMs4918PW
    ysc3839
        5
    ysc3839  
       2023 年 8 月 11 日 via Android
    如果不想手动写明 log 的类型,想要自动判断是否能构造的话,可以用 std::is_constructible ,类似这样:
    template<typename T, typename std::enable_if<std::is_constructible<Str, T>{}, bool>::type = true>
    void log(T obj) {
    Str(obj).print();
    }
    这种方法仍然需要手动把所有继承的类型都写一遍,应该是无法避免的。
    另外这种写法有隐式转换的问题,比如传入 bool float 等类型也会匹配到 int 的。
    timethinker
        6
    timethinker  
       2023 年 8 月 11 日
    如果希望模板根据特定的类型参数进行不同的生成策略,可以使用 Template specialization ,不过还是要看使用场景,有可能不需要模板,只需要重载函数就行了。
    MoYi123
        7
    MoYi123  
       2023 年 8 月 11 日
    如果你的需求就是 print, 看看我写的这个 https://github.com/mmooyyii/mmooyyii/blob/master/codes/print.cpp

    template 和继承没多大关系. 感觉你需要再学一遍 template
    hankai17
        8
    hankai17  
       2023 年 8 月 11 日
    一楼就挺好 面向编译期编程 直接暴力
    三楼也挺好 类型擦除
    geelaw
        9
    geelaw  
       2023 年 8 月 11 日
    针对追加的具体问题

    struct Base { void print() { } };
    struct Str : Base { Str(char const *s) { } };
    struct Num : Base { Num(int v) { } };

    Base log_deduce(Base obj) { return obj; }
    Str log_deduce(Str obj) { return obj; }
    Str log_deduce(char const *s) { return s; }
    Num log_deduce(Num obj) { return obj; }
    Num log_deduce(int v) { return v; }

    template <typename... T>
    void log_impl(T... args)
    {
    /* fold expression from C++17 */
    ((void)(args.print()), ...);
    }

    template <typename... T>
    void log(T... args)
    {
    log_impl(log_deduce(args)...);
    }

    int main()
    {
    log("hello");
    log(123);
    log(123, "hello");
    }

    但我感觉楼主的提供的例子离真实用例很远。
    ysc3839
        10
    ysc3839  
       2023 年 8 月 11 日 via Android
    “能直接指定模板参数的类型集合就好了,例如 Str | Num”
    理论上用可变参数模板+std::is_constructible 是可以实现的,不过写起来感觉会挺复杂的,不建议这么做。
    lovelylain
        11
    lovelylain  
       2023 年 8 月 11 日
    看代码:
    ```c++
    #include <iostream>

    struct Base {
    template<typename T>
    explicit Base(T v) : type("Base") {}
    Base(const char* type) : type(type) {}
    void print() {
    std::cout << type << std::endl;
    }
    private:
    const char* type;
    };

    struct Str : Base {
    Str(const char* s):Base("Str") {}
    };

    struct Num : Base {
    Num(int v):Base("Num") {}
    };

    template <typename T>
    struct LogHelper {};

    template<>
    struct LogHelper<Base> {
    static void print(Base obj) { obj.print(); }
    };

    template<>
    struct LogHelper<const char*> {
    static void print(const char* s) { Str(s).print(); }
    };

    template<>
    struct LogHelper<int> {
    static void print(int v) { Num(v).print(); }
    };

    template<typename T>
    void log(T obj) {
    LogHelper<T>::print(obj);
    }

    int main() {
    log("hello");
    log(123);
    //log(1.23);
    log(Base(1.23));
    return 0;
    }
    ```
    leonshaw
        12
    leonshaw  
       2023 年 8 月 11 日
    定义个 trait ,log 函数里面再构造 obj 呢?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1620 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 00:01 PVG 08:01 LAX 16:01 JFK 19:01
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86