В процессе уточнения стандарта C ++ 11 кажется, что is_trivially_destructible считалось лучшим / более последовательным именем, чем has_trivial_destructor.
Это относительно недавняя разработка, так как мой g ++ 4.7.1 все еще использует старое имя, и оно было исправлено, чтобы соответствовать стандарту с 4.8:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52702
Я лениво использую #if
который поддерживает компилятор, на котором я работаю:
#if TRIVIAL_DESTRUCTOR_TYPE_TRAIT_MATCHES_STANDARD
template<class T>
using is_trivially_destructible = std::is_trivially_destructible<T>;
#else
template<class T>
using is_trivially_destructible = std::has_trivial_destructor<T>;
#endif
…но теперь я пытаюсь поделиться источником с 4.8 пользователями и другими компиляторами, гоняющимися за стандартом. Есть ли лучший способ сделать обнаружение ситуации более «автоматическим» и не требовать #define?
Это работает для меня с GCC 4.7 и 4.8, правильно говоря мне, предоставляется ли старая или новая черта:
#include <type_traits>
namespace std
{
template<typename> struct has_trivial_destructor;
template<typename> struct is_trivially_destructible;
}
template<typename T>
class have_cxx11_trait_helper
{
template<typename T2, bool = std::is_trivially_destructible<T2>::type::value>
static std::true_type test(int);
template<typename T2, bool = std::has_trivial_destructor<T2>::type::value>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) type;
};
template<typename T>
struct have_cxx11_trait : have_cxx11_trait_helper<T>::type
{ };
int main()
{
static_assert( have_cxx11_trait<int>::value, "new trait" );
}
Нотабене Я объявляю (но не определяю) оба признака, потому что стандартная библиотека (вероятно) не объявляет оба, и если имя даже не объявлено, вы не можете обратиться к std::is_trivially_destructible
, Поэтому я объявляю их обоих, но будет использоваться только тот, который определен библиотекой. Добавление объявлений в пространство имен std
это технически неопределенное поведение, поэтому используйте его на свой страх и риск (хотя в этом случае он вряд ли уничтожит ваш жесткий диск).
К сожалению, старый компилятор, который не обеспечивает новую черту, может не справиться с кодом — я не проверял, работает ли он с GCC 4.6
Теперь вы можете определить свою собственную портативную черту:
template<typename T>
using is_trivially_destructible
= typename std::conditional<have_cxx11_trait<T>::value,
std::is_trivially_destructible<T>,
std::has_trivial_destructor<T>>::type;
Семантика has_trivial_destructor
это не то же самое, что новая черта, но это разумное приближение для старых компиляторов, которые не поддерживают новую черту.
В качестве альтернативы вы можете использовать статический полиморфизм для получения другого кода в зависимости от того, какая черта типа доступна, например, специализируя шаблоны или перегружая и отправляя теги, вот так:
template<typename T>
void foo_helper(const T&, std::true_type)
{
// code that uses std::is_trivially_destructible
}
template<typename T>
void foo_helper(const T&, std::false_type)
{
// different code using std::has_trivial_destructor
}
template<typename T>
void foo(const T& t)
{
// do common stuff
// stuff that depends on trait
foo_helper(t, has_cxx11_trait<T>{});
// more common stuff
}
При создании этого ответа не пострадали макросы.
Вот очень Hackish и официально UB фрагмент, который может проверить, является ли std
пространство имен has_trivial_destructor
имя и имеет trivially_destructible
псевдоним черты подобрать правильную черту для проверки (is_trivially_destructible
в этом случае has_trivial_destructor
не доступен).
#include <type_traits>
template<class>
struct has_trivial_destructor{ using test_fail = int; };
template<class>
struct is_trivially_destructible{ using test_fail = int; };
// very hackish and officially UB
namespace std{
template<class T>
struct inherit_htd : has_trivial_destructor<T>{};
template<class T>
struct inherit_itd : is_trivially_destructible<T>{};
}
namespace check_htd{
template<class T>
struct sfinae_false : ::std::false_type{};
template<class T>
auto test(int) -> sfinae_false<typename ::std::inherit_htd<T>::test_fail>;
template<class>
auto test(...) -> ::std::true_type;
struct htd_available : decltype(test<int>(0)){};
}
template<class T>
using Apply = typename T::type;
template<class C, class T, class F>
using If = Apply<std::conditional<C::value,T,F>>;
template<class T>
using trivially_destructible = If<check_htd::htd_available, std::inherit_htd<T>, std::inherit_itd<T>>;
У меня есть похожие проблемы, и ранее я проверял макросы версии GCC (к сожалению, нет способа проверить правильность версии libstd ++, доступен только код даты).
Предыдущий ответ Джонатана Уэйкли является хорошим решением, но не работает с libc ++ и, возможно, с другими библиотеками, которые определяют шаблоны в версионном встроенном пространстве имен или отображают это пространство имен в std с помощью. Таким образом, прототипы не подходят.
Чтобы сделать код из Jonathan Wakely подходящим, вам нужно проверить libc ++ и определить правильное пространство имен.
#include <type_traits>#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
_LIBCPP_BEGIN_NAMESPACE_STD
#else
namespace std {
#endif
template<typename> struct has_trivial_destructor;
template<typename> struct is_trivially_destructible;
// All unimplemented in gcc 4.9
template<typename, typename...> struct is_trivially_constructible;
template<typename> struct is_trivially_default_constructible;
template<typename> struct is_trivially_copy_constructible;
template<typename> struct is_trivially_move_constructible;
template<typename> struct is_trivially_assignable;
template<typename> struct is_trivially_copy_assignable;
template<typename> struct is_trivially_move_assignable;
template<typename> struct is_trivially_copyable;
#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
_LIBCPP_END_NAMESPACE_STD
#else
} // namespace std
#endif
template<typename T>
class have_cxx11_trait_helper
{
template<typename T2, bool = std::is_trivially_destructible<T2>::type::value>
static std::true_type test(int);
template<typename T2, bool = std::has_trivial_destructor<T2>::type::value>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) type;
};
template<typename T>
struct have_cxx11_trait : have_cxx11_trait_helper<T>::type
{ };
int main()
{
static_assert( have_cxx11_trait<int>::value, "new trait" );
}