如何知道模板<类型名称类型>是什么类型?

How can I find out what type the template<typename type> is?

本文关键字:类型 gt 是什么 何知道 lt      更新时间:2023-10-16

假设我有这样的代码例如:

#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++不支持部分函数模板特化。

  • 如果你真的希望使用部分特化(也许是因为你真的需要使用模板,但只消除一个或一些参数的歧义),那么你可以使用类模板部分特化。

  • 我在这里提供的函数模板专门化也实现了一个非专门化的函数模板实例化。这是您没有特别提到的任何类型的包罗万象。消除该捕获将导致任何不受支持的类型出现编译器错误。在许多情况下,这是一件好事。

  • 如果我发现自己在一个真实的系统中编写上面的代码,我可能会消除它并使用简单的非模板函数重载来代替。对于这样一个简单的任务,函数模板专门化是一个非常花哨的锤子。

遗憾的是你不能部分专门化一个函数模板。这就给我们留下了四种方法:

  1. 常规函数重载
  2. 标记分派
  3. SFINAE
  4. 类模板特化

常规的函数重载对于你的示例代码来说是可行的,但是对于更复杂的实际代码来说可能就行不通了。其思想是创建两个函数模板,其中所需的参数类型作为普通参数:

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

你可以在这里找到代码。它适用于任何类型