C++ 识别模板中的原始类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/580922/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:04:00  来源:igfitidea点击:

Identifying primitive types in templates

c++templates

提问by Ben

I am looking for a way to identify primitives types in a template class definition.

我正在寻找一种方法来识别模板类定义中的基元类型。

I mean, having this class :

我的意思是,有这门课:

template<class T>
class A{
void doWork(){
   if(T isPrimitiveType())
     doSomething();
   else
     doSomethingElse(); 
}
private:
T *t; 
};

Is there is any way to "implement" isPrimitiveType().

有什么方法可以“实现”isPrimitiveType()。

回答by Ferdinand Beyer

UPDATE: Since C++11, use the is_fundamentaltemplate from the standard library:

更新:从 C++11 开始,使用is_fundamental标准库中的模板:

#include <type_traits>

template<class T>
void test() {
    if (std::is_fundamental<T>::value) {
        // ...
    } else {
        // ...
    }
}


// Generic: Not primitive
template<class T>
bool isPrimitiveType() {
    return false;
}

// Now, you have to create specializations for **all** primitive types

template<>
bool isPrimitiveType<int>() {
    return true;
}

// TODO: bool, double, char, ....

// Usage:
template<class T>
void test() {
    if (isPrimitiveType<T>()) {
        std::cout << "Primitive" << std::endl;
    } else {
        std::cout << "Not primitive" << std::endl;
    }
 }

In order to save the function call overhead, use structs:

为了节省函数调用开销,使用结构:

template<class T>
struct IsPrimitiveType {
    enum { VALUE = 0 };
};

template<>
struct IsPrimitiveType<int> {
    enum { VALUE = 1 };
};

// ...

template<class T>
void test() {
    if (IsPrimitiveType<T>::VALUE) {
        // ...
    } else {
        // ...
    }
}

As others have pointed out, you can save your time implementing that by yourself and use is_fundamental from the Boost Type Traits Library, which seems to do exactly the same.

正如其他人指出的那样,您可以节省自己实现的时间,并使用 Boost Type Traits Library 中的 is_fundamental,这似乎完全相同。

回答by Anton Gogolev

Boost TypeTraitshas plenty of stuff.

Boost TypeTraits有很多东西。

回答by Rubens

I guess this can do the job quite nicely, without multiple specializations:

我想这可以很好地完成这项工作,无需多种专业化:

# include <iostream>
# include <type_traits>

template <class T>
inline bool isPrimitiveType(const T& data) {
    return std::is_fundamental<T>::value;
}

struct Foo {
    int x;
    char y;
    unsigned long long z;
};


int main() {

    Foo data;

    std::cout << "isPrimitiveType(Foo): " << std::boolalpha
        << isPrimitiveType(data) << std::endl;
    std::cout << "isPrimitiveType(int): " << std::boolalpha
        << isPrimitiveType(data.x) << std::endl;
    std::cout << "isPrimitiveType(char): " << std::boolalpha
        << isPrimitiveType(data.y) << std::endl;
    std::cout << "isPrimitiveType(unsigned long long): " << std::boolalpha
        << isPrimitiveType(data.z) << std::endl;

}

And the output is:

输出是:

isPrimitiveType(Foo): false  
isPrimitiveType(int): true  
isPrimitiveType(char): true  
isPrimitiveType(unsigned long long): true

回答by Rubens

The following example (first posted in comp.lang.c++.moderated) illustrates using partial specialisation to print things differently depending on whether or not they are built-in types.

下面的示例(首次发布在 comp.lang.c++.moderated 中)说明了使用部分特化来打印不同的内容,具体取决于它们是否为内置类型。

// some template stuff
//--------------------
#include <iostream>
#include <vector>
#include <list>

using namespace std;

// test for numeric types
//-------------------------
template <typename T> struct IsNum {
    enum { Yes = 0, No = 1 };
};


template <> struct IsNum <int> { 
    enum { Yes = 1, No = 0 };
};


template <> struct IsNum <double> {
    enum { Yes = 1, No = 0 };
};

// add more IsNum types as required

// template with specialisation for collections and numeric types
//---------------------------------------------------------------
template <typename T, bool num = false> struct Printer {
    void Print( const T & t ) {
        typename T::const_iterator it = t.begin();
        while( it != t.end() ) {
            cout << *it << " ";
            ++it;
        }
        cout << endl;
    }
};

template <typename T> struct Printer <T, true> {
    void Print( const T & t ) {
        cout << t << endl;
    }
};

// print function instantiates printer depoending on whether or
// not we are trying to print numeric type
//-------------------------------------------------------------
template <class T> void MyPrint( const T & t ) {
    Printer <T, IsNum<T>::Yes> p;
    p.Print( t );
}

// some test types
//----------------
typedef std::vector <int> Vec;
typedef std::list <int> List;

// test it all
//------------
int main() {

    Vec x;
    x.push_back( 1 );
    x.push_back( 2 );
    MyPrint( x );        // prints 1 2

    List y;
    y.push_back( 3 );
    y.push_back( 4 );
    MyPrint( y );        // prints 3 4

    int z = 42;
    MyPrint( z );        // prints 42

    return 0;
}

回答by Rafael Baptista

There is a better way - using SFINAE. With SFINAE you don't have to list out every primitive type. SFINAE is a technique that depends on the idea that when template specialization fails it falls back to a more general template. ( it stands for "Specialization failure is not an error" ).

有一个更好的方法 - 使用 SFINAE。使用 SFINAE,您不必列出每个原始类型。SFINAE 是一种技术,它依赖于这样一种思想,即当模板特化失败时,它会回退到更通用的模板。(它代表“专业化失败不是错误”)。

Also you don't really define if you consider a pointer to be a primitive type, so I'll make you templates for all the combinations.

此外,您并没有真正定义是否将指针视为原始类型,因此我将为您制作所有组合的模板。

// takes a pointer type and returns the base type for the pointer.
// Non-pointer types evaluate to void.
template < typename T > struct DePtr                       { typedef void R; }; 
template < typename T > struct DePtr< T * >                { typedef T R; };
template < typename T > struct DePtr< T * const >          { typedef T R; };
template < typename T > struct DePtr< T * volatile >       { typedef T R; };
template < typename T > struct DePtr< T * const volatile > { typedef T R; };

// ::value == true if T is a pointer type
template < class T > struct IsPointer                        { enum { value = false }; };
template < class T > struct IsPointer < T *                > { enum { value = true };  };
template < class T > struct IsPointer < T * const          > { enum { value = true };  };
template < class T > struct IsPointer < T * volatile       > { enum { value = true };  };
template < class T > struct IsPointer < T * const volatile > { enum { value = true };  };

// ::value == true if T is a class type. ( class pointer == false )
template < class T > struct IsClass 
{
   typedef u8 yes; typedef u16 no; 
   template < class C >    static yes isClass( int C::* );
   template < typename C > static no  isClass( ... );
   enum { value = sizeof( isClass<T>( 0 )) == sizeof( yes ) };
};

// ::value == true if T* is a class type. ( class == false )
template < class T > struct IsClassPtr 
{
   typedef u8 yes; typedef u16 no; 
   template < class C >    static yes isClass( int C::* );
   template < typename C > static no  isClass( ... );
   enum { value = sizeof( isClass< typename DePtr< T >::R >( 0 )) == sizeof( yes ) };
};

// ::value == true if T is a class or any pointer type - including class and non-class pointers.
template < class T > struct IsClassOrPtr : public IsClass<T> { };
template < class T > struct IsClassOrPtr < T *                > { enum { value = true };  };
template < class T > struct IsClassOrPtr < T * const          > { enum { value = true };  };
template < class T > struct IsClassOrPtr < T * volatile       > { enum { value = true };  };
template < class T > struct IsClassOrPtr < T * const volatile > { enum { value = true };  };


template < class T > struct IsClassOrClassPtr : public IsClass<T> { };
template < class T > struct IsClassOrClassPtr < T *                > : public IsClassPtr< T*                > { };
template < class T > struct IsClassOrClassPtr < T * const          > : public IsClassPtr< T* const          > { };
template < class T > struct IsClassOrClassPtr < T * volatile       > : public IsClassPtr< T* volatile       > { };
template < class T > struct IsClassOrClassPtr < T * const volatile > : public IsClassPtr< T* const volatile > { };

回答by Beno?t

It cannot be done exactly the way you asked. Here is how it can be done :

它不能完全按照您的要求完成。这是如何做到的:

template<class T>
class A{
void doWork(){
   bool isPrimitive = boost::is_fundamental<T>::value;
   if(isPrimitive)
     doSomething();
   else
     doSomethingElse(); 
}
private:
T *t; 
};

You may get a warning if you put the value of isPrimitive directly inside the ifstatement. This is why i introduced a temporary variable.

如果将 isPrimitive 的值直接放在if语句中,可能会收到警告。这就是我引入临时变量的原因。

回答by Anonymous

Yet another similar examples:

还有一个类似的例子:

#include  <boost/type_traits/is_fundamental.hpp>
#include <iostream>

template<typename T, bool=true>
struct foo_impl
{
    void do_work()
    {
        std::cout << "0" << std::endl;
    }
};
template<typename T>
struct foo_impl<T,false>
{
    void do_work()
    {
        std::cout << "1" << std::endl;
    }
};

template<class T>
struct foo
{
    void do_work()
    {
        foo_impl<T, boost::is_fundamental<T>::value>().do_work();
    }
};


int main()
{
    foo<int> a; a.do_work();
    foo<std::string> b; b.do_work();
}

回答by Andrew Khosravian

Assuming by 'Primitive Type' you mean the built in types you can do a series of template specializations. Your code would become:

假设“原始类型”是指可以执行一系列模板特化的内置类型。你的代码会变成:

template<class T>
struct A{
    void doWork();
private:
    T *t; 
};

template<> void A<float>::doWork()
{
    doSomething();
}

template<> void A<int>::doWork()
{
    doSomething();
}

// etc. for whatever types you like

template<class T> void A<T>::doWork()
{
    doSomethingElse();
}