C++模板化类,根据类型具有不同的行为
C++ templated class with different behaviour depending on type
假设你有一个这样的类:
template <typename T>
class MyClass {
public:
void doSomething();
};
在方法中,有没有办法根据这样的类型具有不同的行为:
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
if(T == int) {
int myInt = atoi(value.c_str());
} else if(T == std::string) {
std::string myString = value;
}
}
有没有简单的方法可以实现这一目标?
我知道我可以编写一个包装类来提供一个接受std::string
的构造函数,所以我可以使用类似的东西:
T myVar(value);
但是,如果使用像int
这样的简单类型,那会更浪费资源,我也想意识到if-else语句能够执行不同的操作。
我将不胜感激的回答。 问候
塔格利希特
在 C++17 中,您可以使用if constexpr(...)
实现您想要的:
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
if constexpr(std::is_same_v<T, int>) {
int myInt = atoi(value.c_str());
} else constexpr(std::is_same_v<T, std::string>) {
std::string myString = value;
}
}
在 C++14 中,您可以非常轻松地实现自己的static_if
。我在CppCon 2016和Meeting C++ 2016上对此进行了教程演讲。
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
static_if(std::is_same_v<T, int>)
.then([&](auto) {
int myInt = atoi(value.c_str());
})
.else_if(std::is_same_v<T, std::string>)
.then([&](auto) {
std::string myString = value;
})();
}
在 C++11 中,您可能希望使用函数重载或模板专用化。前者的示例(在评论中使用 Jarod42 的建议进行了更新):
template <typename> struct tag { };
void call_dispatch(...) { } // lowest priority
void call_dispatch(tag<std::string>)
{
std::string myString = value;
}
void call_dispatch(tag<int>)
{
int myInt = atoi(value.c_str());
}
用法:
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
call_dispatch(tag<T>{});
}
尚未举例说明的两种方法是 1)标签调度和 2)模板专用化
标签调度:
- 调用使用重载的帮助程序函数
我们这样做的方法是将一些空的模板化结构定义为轻量级的"标签"类型:
template <typename T>
class MyClass {
public:
void doSomething();
private:
// tag for dispatch
template<class U>
struct doSomethingTag{};
。然后定义采用该标记专用化的帮助程序函数:
void doSomethingHelper(doSomethingTag<std::string>, std::string value);
void doSomethingHelper(doSomethingTag<int>, std::string value);
};
然后我们可以使用主入口点 (doSomething
) 作为调用专用帮助程序函数的一种方式:
template <typename T>
void MyClass<T>::doSomething() {
//dispatch to helper function
doSomethingHelper(doSomethingTag<T>{}, "17");
}
演示
帮助程序函数如下所示:
template <typename T>
void MyClass<T>::doSomethingHelper(doSomethingTag<std::string>, std::string value)
{
int myInt = atoi(value.c_str());
}
template <typename T>
void MyClass<T>::doSomethingHelper(doSomethingTag<int>, std::string value)
{
std::string myString = value;
}
这应该不会给标记结构带来开销,因为帮助程序函数实际上并不使用 tag 参数,因此它将被优化掉。如果不是,则构造结构的开销为 1 字节。(注:维托里奥·罗密欧在回答中暗示了这一点,但他称之为"函数重载")
模板专用化
你可以"专用化"函数的模板,只要你完全专用于它(指定类的类型!
专用化的语法看起来有点奇怪,因为我们保留尖括号为空:<>
:
template<>
void MyClass<int>::doSomething() {
//dispatch to helper function
std::string value = "17";
int myInt = atoi(value.c_str());
}
template <>
void MyClass<std::string>::doSomething()
{
std::string value = "17";
std::string myString = value;
}
这使您能够保留较小的类:
template <typename T>
class MyClass {
public:
void doSomething();
};
演示 2
结论:
在您给出的具体示例中,我更喜欢模板专业化。但是,通常您希望执行某种部分专用化,这对于成员函数来说非常非常奇怪,因此标签调度通常是首选。
对于C++17,使用constexpr if肯定会让生活更轻松。
这个怎么样:
#include <type_traits>
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
if(std::is_same<T,int>::value) {
int myInt = atoi(value.c_str());
} else if(std::is_same<T,std::string>::value) {
std::string myString = value;
}
}
这是C++11。你可以用 C++17 和constexpr if
让它变得更好。这里有一个问题,即您必须强制转换不可隐式转换的值。例如,必须将std::string
的返回转换为 int 才能编译。
- ArduinoJson 6.15.2:JsonObject没有命名类型
- 防止主数据类型C++的隐式转换
- 大量序列中核苷酸类型的快速计数
- 如何从C++中的依赖类型中获得它所依赖的类型
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如何获取std::result_of函数的返回类型
- 从父命名空间重载类型
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- Openssl 1.1.1d无效使用不完整的类型"struct dsa_st"
- 访问者访问变体并返回不同类型时出错
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 模板元程序查找相似的连续类型名称
- 是否可以从int转换为enum类类型
- 构造函数正在调用一个使用当前类类型的函数
- 我应该使用什么来代替void作为变体中的替代类型之一
- 类中的字符串不命名类型