Frage Beschränkung der Verwendung von C ++ - Vorlagen auf POD-Typen


Ich habe eine C ++ Template-Klasse, die nur korrekt funktioniert, wenn der typisierte Typ einfach alte Daten ist. Alles mit einem Konstruktor, der etwas tut, wird nicht korrekt funktionieren.

Ich möchte irgendwie eine Compiletime oder Laufzeitwarnung bekommen, wenn jemand es trotzdem versucht.

//this should generate error
myclass<std::string> a;

//this should be fine
myclass<int> b;

Gibt es einen Trick, dies zu tun?


21
2017-10-03 08:14


Ursprung


Antworten:


#include <type_traits>

template<typename T>
class myclass
{
    static_assert(std::is_pod<T>::value, "T must be POD");

    // stuff here...
};

Das oben genannte verursacht einen Kompilierungsfehler, wenn Sie einen Nicht-POD-Typ als Vorlageparameter übergeben. Diese Lösung erfordert C ++ 11 für die <type_traits> Kopfzeile und static_assert Stichwort.

EDIT: Sie können dies auch in C ++ 03 implementieren, wenn Ihr Compiler TR1 unterstützt (die meisten tun):

#include <tr1/type_traits>

template<typename T>
class myclass
{
    static char T_must_be_pod[std::tr1::is_pod<T>::value ? 1 : -1];

    // stuff here...
};

33
2017-10-03 08:15



Wenn Sie C ++ 11 Unterstützung haben, sollte std :: is_pod genau das tun, was Sie brauchen. Verwenden Sie es mit std :: enable_if oder mit Tag-Versand. Zum Beispiel so etwas:

template <typename T, typename Enable = void>
class Test;

template<typename T>
class Test<T, typename std::enable_if<std::is_pod<T>::value, void>::type>
{};

int main() {
    Test<int> t1;
    //Test<std::string> t2; <-this will not compile
}

10
2017-10-03 08:16



Während static_assert wahrscheinlich genügt in den meisten Fällen die Verwendung enable_if und der Tag-Versand gibt den Benutzern Ihrer Klasse eine größere Flexibilität durch die Möglichkeiten von SFINAE. Erwägen:

#include <type_traits>
#include <string>
#include <iostream>
template <typename T,
    class=typename std::enable_if< std::is_pod<T>::value >::type>
struct myclass
{
    typedef T value_type;
    T data;
};

template <typename T>
void enjoy(T)
{
    std::cout << "Enjoying T!" << std::endl;
}

template <typename T>
void enjoy(typename myclass<T>::value_type)
{
    std::cout << "Enjoying myclass<T>::value_type!" << std::endl;
}

int main()
{
    enjoy<int>(int()); // prints: Enjoying myclass<T>::value_type!
    enjoy<std::string>(std::string()); // SFINAE at work - prints: enjoying T!
    myclass<int> i; // compiles OK
    //myclass<std::string> s; // won't compile - explicit instantiation w/non-POD!
}

Wenn Sie nun das 2. Template-Argument von entfernen myclass Definition, und stattdessen, wie andere vorgeschlagen haben, fügen Sie ein

  static_assert(std::is_pod<T>::value, "POD expected for T");

innerhalb der Klasse, die zweite Zeile in main() wird nur nicht kompiliert und löst den static_assert aus.

Das heißt, die Fehler von static_assert sind für den menschlichen Beobachter viel freundlicher, als diejenigen von den gescheiterten enable_if. Also, wenn static_assert arbeitet für Sie, gehen Sie dafür. Andernfalls, wenn Sie mit generischer Programmierung in Ihrer Klasse freundlicher sein müssen, sollten Sie einen erläuternden Kommentar hinzufügen enable_if:

 // POD expected for T
 class=typename std::enable_if< std::is_pod<T>::value >::type>

es sei denn, jeder um dich herum ist C ++ 11-fließend.

Im wirklichen Leben ist es eine gute Idee zu erklären Warum T muss für beides sein static_assert und für die Kommentartexte.


5
2017-10-03 09:38



Wenn Sie nicht C ++ 11 haben

Wenn die angestrebten POD-Typen begrenzt sind (int, float, ...) Sie können die Implementierung in ein .cpp Datei und explizit instanziieren es für diese Typen:

.h Datei:

template <typename T>
class myclass
{
    T data;
public:
    void func();
};

.cpp Datei:

#include "myclass.h"

template <typename T>
void myclass<T>::func()
{
}

template class myclass<float>;
template class myclass<int>;
template class myclass<char>;
...

Danach, myclass ist nur für diese Arten verwendbar und bricht für andere.


4
2017-10-03 08:19



Mit type_traits und static_assert ist das ganz einfach:

#include <type_traits>

struct A{
};
struct B{
    virtual ~B(){}
};

template< class T >
struct MyClass
{
    static_assert( std::is_pod<T>::value, "not a POD" );
};

int main()
{
    MyClass<A> a;
    //MyClass<B> b; -- break, cause not a POD
}

0
2017-10-03 08:20