如何找出<typename type =“”>模板的类型?</typename>

时间:2014-08-14 12:16:19

标签: c++ templates

假设我有这个代码,例如:

#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的类型?

4 个答案:

答案 0 :(得分:6)

您可以使用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
}

请注意,即使非活动分支仍然由编译器处理,因此必须在语法和语义上有效(即使它们永远不会被执行,因此它们可能包含运行时错误)。

如果这对你来说是一个问题(即你只需要编译相关部分),你就必须使用一个&#34;代表来上课&#34;特技:

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;
  }
};

答案 1 :(得分:1)

您可以为要处理的每种类型设置一个实例,而不是一个处理所有类型的函数模板的实例化。这可以通过函数模板特化,类模板特化或类模板部分特化来完成。你也可以 - 而且经常应该 - 根本不使用模板,只是简单地提供了重载的非模板函数重载。这是功能模板专业化的一个例子:

#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-string\n";
}

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*。我在上面提供了这个专业:

    模板&lt;&gt; void doWhatever(const char * a) {   std :: cout&lt;&lt; “一个C字符串\ n”; }

  • 我在上面的实现中删除了第二个未使用的模板参数。这是因为C ++不支持部分函数模板特化。这个事实还有一些值得注意的事情。

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

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

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

答案 2 :(得分:0)

可悲的是,你不能部分专门化一个功能模板。这使我们基本上有四种方法可以做到这一点:

  1. 常规功能重载
  2. 代码发送
  3. SFINAE
  4. 班级模板专业化
  5. 常规函数重载可以为您的示例代码提供技巧,但可能无法将其剪切为更复杂的实际代码。我们的想法是将两个具有所需参数类型的函数模板作为普通参数:

    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;
    }
    

    标签发送可能是最好的方法。我们的想法是创建一个元函数(即具有嵌套类型别名的类模板&#34; Type&#34;)并根据该标记的类型重载:

    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,但可转换为例如。也可以为一个标签选择输入的类别。

    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;
    }
    

    安德鲁斯回答中详细描述了类模板专业化。

答案 3 :(得分:0)

为什么不使用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

您可以找到代码here。它适用于任何类型。