Подтвердить что ты не робот

Ограничение использования шаблона С++ для типов POD

У меня есть шаблон шаблона С++, который работает только корректно, если шаблонный тип - это простые старые данные. Все, что имеет конструктор, который ничего не делает, будет работать неправильно.

Я хотел бы как-нибудь получить предупреждение о компиляции или времени выполнения, когда кто-то пытается это сделать.

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

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

Есть ли уловка для этого?

4b9b3361

Ответ 1

#include <type_traits>

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

    // stuff here...
};

Приведенное выше приведет к ошибке компиляции, если вы передадите не-POD-тип в качестве параметра шаблона. Для этого решения требуется С++ 11 для заголовка <type_traits> и static_assert.

EDIT: вы также можете реализовать это в С++ 03, если ваш компилятор поддерживает TR1 (большинство из них):

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

Ответ 2

Если у вас есть поддержка С++ 11, std:: is_pod должен делать именно то, что вам нужно. Используйте его с std:: enable_if или с отправкой тега. Например, что-то вроде этого:

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
}

Ответ 3

В то время как static_assert, вероятно, достаточно в большинстве случаев, использование enable_if и отправки тегов дает большую гибкость пользователям вашего класса способами SFINAE. Рассмотрим:

#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!
}

Теперь, если вы удалите аргумент 2-го шаблона из определения myclass, а вместо этого, как и другие, предложите добавить

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

внутри класса, вторая строка в main() просто не скомпилируется, запуская static_assert.

Тем не менее, ошибки от static_assert гораздо более дружелюбны к человеческому наблюдателю, чем ошибки от неудачного enable_if. Итак, если static_assert работает на вас, подойдите к нему. В противном случае, если вам нужно быть более дружелюбным к универсальному программированию вокруг вашего класса, подумайте о добавлении пояснительного комментария вокруг enable_if:

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

если все вокруг вас не являются С++ 11-fluent.

В реальной жизни неплохо объяснить, почему T должен быть POD как для static_assert, так и для текстов комментариев.

Ответ 4

Если у вас нет С++ 11

Если целевые типы POD ограничены (int, float,...) Вы можете поместить реализацию в файл .cpp и явным образом создать экземпляр для этих типов:

.h файл:

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

.cpp файл:

#include "myclass.h"

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

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

После этого myclass можно использовать только для этих типов и разрывов для других.

Ответ 5

С type_traits и static_assert это довольно просто:

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