在模板方法 c++ 中传递结构参数
Passing struct parameter in template method c++
我在将参数传递给类模板时遇到问题
struct Car {
int id;
char *model;
int date;
int cost;
};
template <class T>
class Set
{
private:
int *a;
int _size;
map<int, T> data;
public:
//some methods before
void insert(T x)
{
int num;
if (std::is_same<T, Car>::value)
num = x.id;
if (num >= 0 && num < (_size << 5))
throw "Element is out of set size!";
a[num / 32] = a[num / 32] | (1 << (num % 32));
if (std::is_same<T, Car>::value)
data.insert(make_pair(num, x));
}
};
我的insert
方法应该接受int
类型和Car
结构。但是Visual Studio说我在num = x.id;
有编译错误,x
应该是一个类,结构或联合。我可以传递指向此函数的指针,但我将无法传递像class.insert(5)
这样的整数。我如何解决它或如何使方法接受指向结构体和常规变量的指针,而无需对其中一种类型进行另一个规范?
是的,这是很多人都在努力解决的问题。让我们看看截取的模板方法:
void insert(T x)
{
int num;
if (std::is_same<T, Car>::value)
num = x.id;
//...
这里的问题是if
语句在运行时进行语义计算(即使编译器可以优化分支,因为它会在编译时知道值(,因此,编译器需要"假装"为两个分支生成代码。附带说明一下,当分支未被占用时,您的num
将保持单元化,这是未定义的行为。
在您的情况下,这意味着即使T
不是Car
,语义上仍然需要为num = x.id
生成代码。显然,int.id
不是一个有效的说法。
要使用 C++17 解决此问题,您可以使用if constexpr
:
if constexpr (std::is_same<T, Car>::value) //...
Constexpr if
保证在编译时进行评估,并且不会计算属于未获取分支的代码。
在 C++17 之前的世界中,您通常可以使用标签调度或 SFINAE。我总是更喜欢标签调度,因为我认为它比 SFINAE 更简单。标签调度依赖于使用函数重载,在您的情况下是这样的(因为不清楚您的代码在传递时会做什么int
,我会假装您需要将num
设置为 int passed:
int get_id(const Car& car) {
return car.id;
}
int get_id(int v) {
return v;
}
void insert(const T& t) {
int num = get_id(t);
// ...
请注意,在这种特殊情况下,它甚至不是标签调度,因为情况非常简单。
考虑这个简化的例子:
template <typename T>
struct foo {
T t;
void bar() {
if (std::is_same<T,Car>) { std::cout << t.id; }
}
};
现在你必须记住,模板实例化发生在编译时,即编译器创建foo<Car>
struct foo<Car> {
Car t;
void bar() {
if (true) { std::cout << t.id; }
}
};
到目前为止一切顺利,但是当您为int
实例化相同的模板时,您会得到
struct foo<int> {
int t;
void bar() {
if (false) { std::cout << t.id; }
}
};
问题是,即使条件始终为 false,代码仍然需要编译。int
没有id
,因此您的代码无法编译。
有几种方法可以解决您的问题。您可以使用 SFINAE(如果您不习惯它,可能会有点混乱,就像我一样;)、if constexpr
(避免未采用的分支必须是有效的(或只是明确提供您需要的专业化。
为了使该行甚至编译,您必须使用if constexpr
:
if constexpr(std::is_same<T, Car>::value) num = x.id;
但请注意,从您当前的代码开始,否则num
仍然未初始化(但仍在下一行中使用(。
如果你想能够insert
T
、Car
和int
类型的参数,最合理的方法可能是在三个重载之间分散功能:
void insert(T x);
void insert(Car x);
void insert(int x);
并且另外将模板专用于两种类型Car
和int
,否则将无法编译。
或者你的意思是Set
实际上应该接受所有类型的元素?然后它是一个更棘手的类型。首先,insert
方法本身应该是模板化的:
template<typename T> void insert(T x);
而类本身很可能不应该:
class Set;
(然后在模板化insert
您可以使用此线程中有关if constexpr
的所有内容。接下来,底层容器应该接受所有类型的值,这意味着你需要使用类似map<int, std::any>
的东西(顺便说一句,为什么map
?如果您存储与整数混合的汽车,则按 ID 排序可能不是很一致。你可以在这里使用unordered_map
,甚至可以unordered_set
使用你自己的多类型哈希函数。
最后但并非最不重要的一点是,需要一段时间才能制定一个最小一致的方案来实际检索存储的值。这里的主要问题是,你必须以类型擦除的方式存储它们,所以你需要以某种方式调用存储值的实际类型 - 以一种并非完全不优雅的方式。
这就是您要构建的吗?
- 使用不带参数的函数访问结构元素
- 将结构字段的类型展开为可变模板参数
- MSVC将仅移动结构参数解释为指针
- 如何使我的 sizeof sum 结构与空参数包一起工作
- 如何传递带有通过引用传递的结构参数的函数?
- 展开可变参数模板结构
- 在类构造函数中定义结构变量的参数
- 如何在方法中传递结构参数
- 我应该在C++中将这些结构用作参数化构造函数吗?
- 在模板方法 c++ 中传递结构参数
- 结构参数在函数调用时损坏
- 如何使用结构参数委托构造函数
- 错误 C2783:无法推断结构参数的模板参数
- 用于存储元组键的数据结构:参数关系列表
- 使用结构参数从程序集中调用C++函数
- 如何为llvm IR调用指令创建结构参数
- c++为函数使用结构参数而不是多个参数
- 导入结构参数
- 将结构参数传递给结构内的函数
- 带有结构参数的pthread不起作用