关于 C++ 模板一个问题

272 天前
 iqoo

如下代码,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 类。

1413 次点击
所在节点    程序员
12 条回复
NessajCN
272 天前
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
272 天前
楼主的代码明明是 log 可以接受任何可以 .print 的类型。提的需求也非常不明确。

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

另外,建议想清楚自己的问题,如果不是适合用模板解决的问题的话,会从一个小问题,变成一个报错非常长的问题。
aglargilwangz
272 天前
```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
272 天前
你给的代码编译不了,我用的楼上的。
搞不懂的多用 godbolt
https://godbolt.org/z/cMs4918PW
ysc3839
272 天前
如果不想手动写明 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
272 天前
如果希望模板根据特定的类型参数进行不同的生成策略,可以使用 Template specialization ,不过还是要看使用场景,有可能不需要模板,只需要重载函数就行了。
MoYi123
272 天前
如果你的需求就是 print, 看看我写的这个 https://github.com/mmooyyii/mmooyyii/blob/master/codes/print.cpp

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

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
272 天前
“能直接指定模板参数的类型集合就好了,例如 Str | Num”
理论上用可变参数模板+std::is_constructible 是可以实现的,不过写起来感觉会挺复杂的,不建议这么做。
lovelylain
272 天前
看代码:
```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
272 天前
定义个 trait ,log 函数里面再构造 obj 呢?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/964489

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX