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

2022-04-16 11:40:16 +08:00
 justou

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

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))的方式纵然可行,
 * 但是两个分支代码都是一样的, 仅有以上提到的访问成员不一致的区别, 跟写两个完全重复的模板函数一样.
 * */
}

谢谢各位

2400 次点击
所在节点    C++
10 条回复
ink19
2022-04-16 11:56:53 +08:00
```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
2022-04-16 11:58:59 +08:00
能不能对于每个 data_n 写一个 template 的 getter function ,然后对这两个类 specialize
TuneG
2022-04-16 11:59:01 +08:00
这个,使用 typedef 就会有这种问题,如果不能改成 alias declaration ,那的确没有好方法,标准库中 type trait 也是有这种历史代码使用嵌套在模版化的 struct 里的 typedef 实现的,所以存在两套调用,对应的每种都有对应的别名模版调用方式,例如 std::remove_const<T>::type 的别名模版调用 std::remove_const_t<T>,为啥标准库不统一起来,估计也没什么好方法,不如不统一
ink19
2022-04-16 12:01:28 +08:00
@ink19 应该是这个意思吧?直接加一层封装就好了,感觉函数式编程就是这样,功能都能想办法实现,就是加封装的层数问题
justou
2022-04-16 12:14:18 +08:00
@ink19 是这个意思, 我就知道肯定有办法的, 谢谢~
GeruzoniAnsasu
2022-04-16 12:23:33 +08:00
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
2022-04-16 12:28:15 +08:00
@GeruzoniAnsasu 也是好办法, 谢谢推荐
dangyuluo
2022-04-16 12:56:13 +08:00
虽然用 Traits 可以解决这个问题,但是从另一个角度想,func 似乎不应该同时接受 classA 和 classB ,毕竟一个是 IS-A, 一个是 HAS-A (借用继承的思路)
lujunliang
2022-04-16 16:55:09 +08:00
@GeruzoniAnsasu 请教一下,这里 constexpr _helper 的函数体在什么情况下允许为空的呢?
GeruzoniAnsasu
2022-04-16 21:49:46 +08:00
@lujunliang 模板只有在用到的时候才会实例化,实例化前被剔除掉的话根本不会进入语法检查阶段。 trait 类的所有成员最终只会留下 BaseType**推导结果** 的定义。在这个类里写一个 int a(){return "X";} 编译器都是不管的。所以只把声明留下来不要函数体也可以,反正最后编译器不会去检查这个函数的定义在哪里,只是用声明计算类型而已。

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

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

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

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

© 2021 V2EX