6

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

4

3 回答 3

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" );
}

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

在制作这个答案时没有损害宏。

于 2012-10-03T20:56:42.690 回答
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>>;

活生生的例子。

于 2012-10-03T20:59:15.330 回答
2

我有类似的问题,并且之前检查了 GCC 版本宏(不幸的是,没有办法检查正确的 libstd++ 版本,只有一个日期代码可用)。Jonathan Wakely的上一个答案是一个很好的解决方案,但是对于 libc++ 和可能在版本化的内联命名空间中定义模板或通过 using 将该命名空间映射到 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" );
}
于 2015-10-14T11:59:27.203 回答