“has_trivial_destructor”定义而不是“is_trivially_destructible”

时间:2012-10-03 04:53:00

标签: c++ c++11 g++ conditional-compilation typetraits

在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?

3 个答案:

答案 0 :(得分:10)

这适用于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" );
}

N.B。我声明(但没有定义)这两个特征,因为标准库(可能)不会声明两者,如果名称甚至没有声明,那么你就不能引用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
  }

在回答这个问题时,没有任何宏受到伤害。

答案 1 :(得分:4)

这是一个非常 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>>;

Live example.

答案 2 :(得分:2)

我有类似的问题,之前已经检查过GCC Version Macros(遗憾的是没有办法检查正确的libstd ++版本,只提供日期代码)。 Previous Answer by Jonathan Wakely是一个很好的解决方案,但是使用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" );
}