对于类型修改特征,我是否应该为 typename::transform<...>::type 提供一个模板 typedef(又名使用)方便包装器?

For type modification traits, should I provide a template-typedef (aka using) convenience wrapper for typename::transform<...>::type?

本文关键字:一个 包装 方便 typedef lt 特征 修改 类型 于类型 是否 gt      更新时间:2023-10-16

我想知道有了模板类型的可用性,我应该为转换类型的类提供方便的包装器。考虑这个(无用的)示例:

template< T >
struct whatever
{
   typedef typename std::conditional< sizeof(T) <= sizeof(void*),
                                      int, long >::type type;
};
这里的

std::conditional是标题中的transform,与typename transform<...>::type一起使用。此外,whatever本身也是transform,并且以相同的方式使用。

有了模板类型(又名using),我可以将接口更改为:
template< T >
using whatever = typename std::conditional< sizeof(T) <= sizeof(void*),
                                            int, long >::type;

简化了用法。对于所有这些情况都可以这样做,但是由于需要(部分)专门化,您有时会最终使用实现类和包装器。在std::conditional的情况下,您可能最终将其移动到std::impl::conditional<...>,并提供另一个包装器

namespace std
{
  namespace impl
  {
    // "classic" implementation of std::conditional
  }
  template< bool B, typename T, typename F >
  using conditional = typename impl::conditional< B, T, F >::type;
}
这留下了我应该提供什么接口/API的问题。我看到提供包装器的一个优点:它可以防止用户错误。例如这个问题&答案。

为了保持现有的接口,我看到了以下几点:

  • 一致性。这就是类型特征基本上每个人都在使用
  • 变压器与变换结果的分离。您可以将转换器作为类型传递,在上面的whatever的情况下,这将不再是可能的。
  • 通过防止专门化所需的impl解决方案减少代码。

我想听到支持或反对提供"新"接口的争论,而不仅仅是"我更喜欢第二种方法"这样的意见。我感兴趣的是找出需要一种或另一种方法的情况,或者当它无法工作/扩展时。

老实说,这个问题主要是我缺乏模板类型的经验,所以如果你有一些实际的经验,请分享它的好和坏的一面,以及我是否应该认为typename transform<...>::type API在c++ 11中已经过时了

我总是这样做:

  1. 在一些命名空间(impl, detail等)中有做实际工作的类。
  2. 外部,使用模板别名来清理调用代码。

例如,如果我要重写std::remove_reference,我会这样做:

namespace detail {
    template <typename T> struct remove_reference
    {
        using type = T;
    };
    template <typename T> struct remove_reference<T&>
    {
        using type = T;
    };
    template <typename T> struct remove_reference<T&&>
    {
        using type = T;
    };
}
template <typename T>
using remove_reference = typename detail::remove_reference<T>::type;

我没有编译上面的代码,所以如果我打错了,请不要射我。

一个好处是调用地点更加清晰。一个潜在的缺点是,如果你正在编写高阶泛型代码,并且依赖于某些类型函数(例如

)内部存在的::类型。
template <template <typename...> class F, typename... Ts>
using apply = typename F<Ts...>::type;

不过你可以把using行改成

using apply = F<Ts...>;

没有任何伤害。这一切都归结于代码的其余部分在做什么。如果你的类型函数需要有一个特定的接口(::type或::value),那么你需要遵守它们。