gpt4 book ai didi

c++ - 可以使用模板按名称访问结构变量吗?

转载 作者:IT老高 更新时间:2023-10-28 22:05:53 26 4
gpt4 key购买 nike

假设我有一个这样的结构:

struct my_struct
{
int a;
int b;
}

我有一个函数应该为“a”或“b”设置一个新值。此功能还需要指定要设置的变量。一个典型的例子是这样的:

void f(int which, my_struct* s, int new_value)
{
if(which == 0)
s->a = new_value;
else
s->b = new_value;
}

由于我不会在这里写的原因,我不能将指向 a/b 的指针传递给 f。所以我不能用 my_struct::a 或 my_struct::b 的地址调用 f。我不能做的另一件事是在 my_struct 中声明一个 vector (int vars[2]) 并将一个整数作为索引传递给 f。基本上在 f 中我需要按名称访问变量。

上一个例子的问题是,将来我计划向 struct 添加更多变量,在这种情况下,我会记得向 f 添加更多 if 语句,这不利于可移植性。我可以做的就是把 f 写成一个宏,像这样:

#define FUNC(which)
void f(my_struct* s, int new_value) \
{ \
s->which = new_value; \
}

然后我可以调用 FUNC(a) 或 FUNC(b)。

这可行,但我不喜欢使用宏。所以我的问题是:有没有办法使用模板而不是宏来实现相同的目标?

编辑:我将尝试解释为什么我不能使用指针并且我需要按名称访问变量。基本上,结构包含系统的状态。该系统需要在请求时“撤消”其状态。撤消是使用一个名为 undo_token 的接口(interface)来处理的,如下所示:

class undo_token
{
public:
void undo(my_struct* s) = 0;
};

因此,由于多态性,我无法将指针传递给 undo 方法(mystruct 也包含其他类型的变量)。

当我向结构中添加一个新变量时,我通常也会添加一个新类,如下所示:

class undo_a : public undo_token
{
int new_value;
public:
undo_a(int new_value) { this->new_value = new_value; }
void undo(my_struct *s) { s->a = new_value}
};

问题是我在创建 token 时不知道指向 s 的指针,因此我无法在构造函数中保存指向 s::a 的指针(这将解决问题)。“b”的类是一样的,只是我必须写“s->b”而不是s->a

也许这是一个设计问题:我需要每个变量类型一个撤消标记,而不是每个变量一个...

最佳答案

要回答确切的问题,有,但它相当复杂,而且纯粹是编译时的事情。 (如果您需要运行时查找,请使用指向成员的指针 - 根据您更新的问题,您可能误解了它们的工作原理。)

首先,您需要一些可以在编译时用来表示“成员名称”的东西。在编译时元编程中,除整数之外的所有内容都必须由类型表示。所以你将使用一个类型来代表一个成员。

例如,一个整数类型的成员存储一个人的年龄,另一个用于存储他们的姓氏:

struct age { typedef int value_type; };
struct last_name { typedef std::string value_type; };

然后你需要像 map 这样在编译时进行查找的东西。我们称它为 ctmap。让我们最多支持 8 名成员。首先我们需要一个占位符来表示字段的缺失:

struct none { struct value_type {}; };

然后我们可以前向声明ctmap的形状:

template <
class T0 = none, class T1 = none,
class T2 = none, class T3 = none,
class T4 = none, class T5 = none,
class T6 = none, class T7 = none
>
struct ctmap;

然后我们专门针对没有字段的情况:

template <>
struct ctmap<
none, none, none, none,
none, none, none, none
>
{
void operator[](const int &) {};
};

稍后(可能)会清楚原因。最后,所有其他情况的定义:

template <
class T0, class T1, class T2, class T3,
class T4, class T5, class T6, class T7
>
struct ctmap : public ctmap<T1, T2, T3, T4, T5, T6, T7, none>
{
typedef ctmap<T1, T2, T3, T4, T5, T6, T7, none> base_type;

using base_type::operator[];
typename T0::value_type storage;

typename T0::value_type &operator[](const T0 &c)
{ return storage; }
};

这到底是怎么回事?如果你说:

ctmap<last_name, age> person;

C++ 将通过递归扩展模板为 person 构建一个类型,因为 ctmap 继承自自身,我们为第一个提供存储字段,然后在我们继承时将其丢弃。当没有更多字段时,这一切都会突然停止,因为 all-none 的特化开始了。

所以我们可以说:

person[last_name()] = "Smith";
person[age()] = 104;

这就像在 map 中查找,但在编译时,使用字段命名类作为键。

这意味着我们也可以这样做:

template <class TMember>
void print_member(ctmap<last_name, age> &person)
{
std::cout << person[TMember()] << std::endl;
}

这是一个打印成员值的函数,其中要打印的成员是类型参数。所以我们可以这样调用它:

print_member<age>(person);

所以是的,你可以写一个有点像struct,有点像编译时map的东西。

关于c++ - 可以使用模板按名称访问结构变量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/672843/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com