如下代码,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
NessajCN 2023-08-11 16:22:17 +08:00
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; } |
2
geelaw 2023-08-11 16:27:13 +08:00 2
楼主的代码明明是 log 可以接受任何可以 .print 的类型。提的需求也非常不明确。
你希望模板自动推断存在可以构造的 Base 的子类 T ,还是希望 T 就是参数本身的类型,然后 obj.print 改写为 T1{obj}.print(),其中 T1 是 Base 的某个子类并且可以被 obj 所构造? 另外,建议想清楚自己的问题,如果不是适合用模板解决的问题的话,会从一个小问题,变成一个报错非常长的问题。 |
3
aglargilwangz 2023-08-11 16:27:19 +08:00 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); } ``` |
4
hhjuteman 2023-08-11 16:50:44 +08:00
|
5
ysc3839 2023-08-11 16:52:44 +08:00 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 的。 |
6
timethinker 2023-08-11 17:07:25 +08:00
如果希望模板根据特定的类型参数进行不同的生成策略,可以使用 Template specialization ,不过还是要看使用场景,有可能不需要模板,只需要重载函数就行了。
|
7
MoYi123 2023-08-11 17:38:21 +08:00
如果你的需求就是 print, 看看我写的这个 https://github.com/mmooyyii/mmooyyii/blob/master/codes/print.cpp
template 和继承没多大关系. 感觉你需要再学一遍 template |
8
hankai17 2023-08-11 17:50:46 +08:00
一楼就挺好 面向编译期编程 直接暴力
三楼也挺好 类型擦除 |
9
geelaw 2023-08-11 19:38:29 +08:00
针对追加的具体问题
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"); } 但我感觉楼主的提供的例子离真实用例很远。 |
10
ysc3839 2023-08-11 20:05:55 +08:00 via Android
“能直接指定模板参数的类型集合就好了,例如 Str | Num”
理论上用可变参数模板+std::is_constructible 是可以实现的,不过写起来感觉会挺复杂的,不建议这么做。 |
11
lovelylain 2023-08-11 20:17:01 +08:00
看代码:
```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; } ``` |
12
leonshaw 2023-08-11 22:40:25 +08:00
定义个 trait ,log 函数里面再构造 obj 呢?
|