如何知道模板<类型名称类型>是什么类型?
How can I find out what type the template<typename type> is?
假设我有这样的代码例如:
#include <iostream>
template< typename typeOne , typename typeTwo >
void doWhatever( typeOne a , typeTwo b )
{
if( typeOne == std::string )
{
std::cout << "Hey I found a string" << std::endl;
}
else if( typeOne == int )
{
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
else
{
std::cout << "Are you mad?! What is that?" << std::endl;
}
// if a is string for example cast it to int
}
int main()
{
doWhatever( "123" , 10 );
return 0;
}
这显然行不通。
如何查看函数内typename
的类型?
您可以使用std::is_same
:
#include <type_traits>
template< typename typeOne , typename typeTwo >
void doWhatever( typeOne a , typeTwo b )
{
if( std::is_same<typeOne, std::string>::value )
{
std::cout << "Hey I found a string" << std::endl;
}
else if( std::is_same<typeOne, int>::value )
{
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
else
{
std::cout << "Are you mad?! What is that?" << std::endl;
}
// if a is string for example cast it to int
}
请注意,即使是非活动的分支仍然由编译器处理,因此必须在语法和语义上有效(即使它们永远不会被执行,因此它们可能包含运行时错误)。
如果这对你来说是一个问题(即你只需要编译相关的部分),你必须使用"委托给类"的技巧:
template <typename typeOne, typename typeTwo>
void doWhatever(typeOne a, typeTwo b)
{
doWhatever_helper<typeOne, typeTwo>::do_specific_part(a, b);
do_other_parts();
}
template <typename typeOne, typename typeTwo>
struct doWhatever_helper
{
static void do_specific_part(typeOne a, typeTwo b) {
std::cout << "Are you mad?! What is that?" << std::endl;
}
};
template <typename typeTwo>
struct doWhatever_helper<std::string, typeTwo>
{
static void do_specific_part(std::string a, typeTwo b) {
std::cout << "Hey I found a string" << std::endl;
}
};
template <typename typeTwo>
struct doWhatever_helper<int, typeTwo>
{
static void do_specific_part(int a, typeTwo b) {
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
};
不需要处理所有类型的函数模板的一个实例化,您可以为希望处理的每个类型都有一个实例化。这可以通过函数模板专门化、类模板专门化或类模板部分专门化来实现。您还可以(通常应该)根本不使用模板,而只是提供重载的非模板函数重载。下面是一个函数模板特化的例子:
#include <cstdlib>
#include <string>
#include <iostream>
template< typename typeOne >
void doWhatever( typeOne a);
template <>
void doWhatever <std::string> (std::string a)
{
std::cout << "Hey I found a string" << std::endl;
}
template <> void doWhatever <const char *> (const char* a)
{
std::cout << "A C-stringn";
}
template <>
void doWhatever <int> (int a)
{
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
template <typename typeOne>
void doWhatever (typeOne a)
{
std::cout << "Are you mad?! What is that?" << std::endl;
}
int main()
{
doWhatever( "123" );
return 0;
}
这里有几件事需要注意。
在使用模板时,如果不需要消除模板参数类型的歧义,通常会更好。如果你正在使用模板,你确实需要知道类型,也许你不应该在这里使用模板。更好的方法可能是简单地为您希望支持的类型提供非模板函数的重载。
文字
"123"
不被解释为std::string
,而是const char*
。我在上面提供了这个专门化:template <> void doWhatever (const char* a){std:: cout & lt; & lt;"一个c字符串 n";}
我已经在上面的实现中消除了第二个未使用的模板参数。这是因为c++不支持部分函数模板特化。
如果你真的希望使用部分特化(也许是因为你真的需要使用模板,但只消除一个或一些参数的歧义),那么你可以使用类模板部分特化。
我在这里提供的函数模板专门化也实现了一个非专门化的函数模板实例化。这是您没有特别提到的任何类型的包罗万象。消除该捕获将导致任何不受支持的类型出现编译器错误。在许多情况下,这是一件好事。
如果我发现自己在一个真实的系统中编写上面的代码,我可能会消除它并使用简单的非模板函数重载来代替。对于这样一个简单的任务,函数模板专门化是一个非常花哨的锤子。
遗憾的是你不能部分专门化一个函数模板。这就给我们留下了四种方法:
- 常规函数重载
- 标记分派
- SFINAE
- 类模板特化
常规的函数重载对于你的示例代码来说是可行的,但是对于更复杂的实际代码来说可能就行不通了。其思想是创建两个函数模板,其中所需的参数类型作为普通参数:
template< typename typeTwo >
void doWhatever( std::string a , typeTwo b )
{
std::cout << "Hey I found a string" << std::endl;
}
template< typename typeTwo >
void doWhatever( int a , typeTwo b )
{
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
template< typename typeOne, typename typeTwo >
void doWhatever( typeOne a , typeTwo b )
{
std::cout << "Are you mad?! What is that?" << std::endl;
}
标签分发可能是最好的方法。我们的想法是创建一个元函数(即具有嵌套类型别名"type"的类模板)并基于该标签的类型进行重载:
struct IntTag{};
struct StringTag{};
struct BadTag{};
template<typename T>
struct MakeTag{
using Type = BadTag;
}
template<>
struct MakeTag<int>{
using Type = IntTag;
}
template<>
struct MakeTag<std::string>{
using Type = StringTag;
}
template< typename typeOne , typename typeTwo >
void doHelper( IntTag, typeOne a , typeTwo b ) {
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
template< typename typeOne , typename typeTwo >
void doHelper( StringTag, typeOne a , typeTwo b ) {
std::cout << "Hey I found a string" << std::endl;
}
template< typename typeOne , typename typeTwo >
void doHelper( BadTag, typeOne a , typeTwo b ) {
std::cout << "Are you mad?! What is that?" << std::endl;
}
template< typename typeOne , typename typeTwo >
void doWhatever( typeOne a , typeTwo b ) {
doHelper(typename MakeTag<typeOne>::Type{},a,b);
}
这样做的好处是,用户可以为他们的类型特殊化MakeTag,例如,这种类型不是int型,但可以转换为int型。也可以为单个标记选择输入的类别。
SFINAE是std::enable_if背后的语言特性,其思想是,如果条件为真,则std::enable_if具有嵌套类型,如果不为真,则导致该函数从候选集合中删除。
template< typename typeOne , typename typeTwo, typename = std::enable_if_t<std::is_same<typeOne,std::string>::value> >
void doWhatever( typeOne a , typeTwo b )
{
std::cout << "Hey I found a string" << std::endl;
}
template< typename typeOne , typename typeTwo, typename = std::enable_if_t<std::is_same<typeOne,int>::value> >
void doWhatever( typeOne a , typeTwo b )
{
std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
template< typename typeOne , typename typeTwo, typename = std::enable_if_t<!(std::is_same<typeOne,std::string>::value || std::is_same<typeOne,int>::value)> >
void doWhatever( typeOne a , typeTwo b )
{
std::cout << "Are you mad?! What is that?" << std::endl;
}
类模板特化在Andrews的回答中有很好的描述。
为什么不直接使用typeid中的name()函数呢?下面的print_type函数打印被打乱的符号,但是如果您愿意,也可以打乱它:
#include <cstdlib>
#include <typeinfo>
#include <vector>
#include <string>
#include <iostream>
using std::cout;
using std::endl;
template <class T>
void cppfilt(const T& t) {
const std::type_info& ti = typeid(t);
static const std::string command("c++filt -t ");
system((command + std::string(ti.name())).c_str());
}
template <typename T>
void print_type(T& t) {
cout<<"Type: "<<typeid(t).name()<<endl;
}
int main(){
std::string s("hello world!");
int v = 5;
cout<<"Pring mangled ty:pes"<<endl;
print_type(s);
print_type(v);
cout<<"nDemangle symbols using c++filt"<<endl;
cppfilt(s);
cppfilt(v);
return 0;
}
这段代码输出:
Pring mangled ty:pes
Type: Ss
Type: i
Demangle symbols using c++filt
std::basic_string<char, std::char_traits<char>, std::allocator<char> >
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类类型
- 构造函数正在调用一个使用当前类类型的函数
- eigen :: llt&lt;eigen :: matrixxd&gt;具有不完整的类型
- 错误c++visual studio c2227左侧'->;Init';必须指向类/结构/联合/泛型类型