【问题标题】:Choosing between size_t and container::size_type in compile time在编译时在 size_t 和 container::size_type 之间进行选择
【发布时间】:2012-10-11 10:42:48
【问题描述】:

我想在代码块中选择数据类型时更加迂腐,我需要在一般的 size_type size_t 或容器类型的 container::size_type 之间进行选择。我的问题是,如果我有以下代码块,我不知道该怎么做。有人可以帮忙吗?

template<typename some_container>
int func(some_container& input)
{
    //Some code...
    //...
    decltype(input.size()) variable_x; //Choose this if defined,
    size_t                 variable_x; //otherwise choose this
    //... Some more code...
}

在这种情况下,some_container 可能是一个自定义容器,并且不提供size() 功能。导致我产生这种想法的原因是在size_t vs container::size_type 上阅读size_tcontainer::size_type 之间的区别。我也读过Determine if a type is an STL container at compile time,但这种方法对我的情况来说有点过分。

【问题讨论】:

  • 您在问题中有 size_typesize_t 错误的方式。 ITYM“一般size_tcontainer::size_type 用于容器类型”。
  • auto 在这里派上用场:auto size = input.size()

标签: c++ c++11 visual-studio-2012 typetraits size-type


【解决方案1】:

使用decltype 的方法是正确的,诀窍是使用 SFINAE,这很容易使用模板类或函数重载来完成。我将展示函数方式,因为它在 C++11 中非常简单:

// One helper per size_type source
template <typename T>
auto size_type_alt(T const& t) -> decltype(t.size());

auto size_type_alt(...) -> std::size_t;

// The switch between helpers
template <typename T>
auto size_type_switch() -> decltype(size_type_alt(std::declval<T>{}));

// And syntactic sugar
template <typename T>
using size_type = decltype(size_type_switch<T>());

用法:

template <typename T>
void some_algorithm(T const& t) {
    size_type<T> const size = 0;
    // ...
}

注意:开关和糖衣层可以混合在一起,但是我认为您可能会喜欢单独查看这些步骤。

【讨论】:

  • 我需要在晚上晚些时候处理这​​个问题。这看起来简洁明了,但是,嘿,我正在(重新)学习 C++。 :-) 我也必须通读iammilind 的代码。是的,我确实很欣赏单独的步骤 - 以及它们可以混合在一起的提示。
  • @Veksi:一个友好的建议:花点时间吸收东西,不要在生产代码中使用你不习惯的东西;)
  • 这是爱好代码,幸运的是。但是由于这种原因经常出现,看起来像重复,所以必须练习。顺便说一句,我认为“auto size_type_alt(...) -> std::size_t”可能不会出现在 Visual Studio 2012 上。嗯,我需要将其添加为标签。
【解决方案2】:

以下是确定class 是否包含类型(例如size_type)的一种方法:

template <typename T> 
struct Has_size_type
{
  typedef char (&yes)[2];

  template <typename C> static yes test(typename C::size_type*);
  template <typename> static char test(...);

  static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

以下是两种类型的选择方式:

template<bool> struct Bool;
template<typename T, typename = Bool<true> >
struct Set { typedef size_type type; };
template<typename T>
struct Set<T,Bool<Has_size_type<T>::value> > { typedef typename T::size_type type; };

编辑开始:这是另一种更简单的方法:

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct Set 
{ typedef size_type type; };

template<typename T>
struct Set<T,typename void_<typename T::size_type>::type>
{ typedef typename T::size_type type; };

编辑结束

所以最后,使用如下:

template<typename some_container>
int func(some_container& input)
{
  typedef typename Set<some_container>::type type;
}

所以现在typesize_typesome_container::size_type,如果有的话。

【讨论】:

  • 为什么你的特质类叫Set?这(a)可能是英语中最模棱两可的单词,并且(b)与std::中完全不相关的名称只有一点不同;-p
  • @SteveJessop,这样写是为了传达“有意义”的英文句子:Set Some Container type! :)。我暂时忘记了std::set 也存在(我一直想知道为什么它会这样命名)。 Set 的名称可以是任何名称,...作者的选择!
  • @iammilind std::set 之所以这样命名是因为它是一个集合。
  • 我已经写了 Matthieu 的代码,今天晚些时候我需要处理这个问题(没有不可预见的障碍)。但我稍后会回来。
  • 真的很难在这个版本和马蒂厄的版本之间做出决定,但我会选择这个版本,如果不是因为第一个并提供代码链接的话。谢谢!
【解决方案3】:

如果它没有size_type,那么它就不是一个容器。期间。

标准要求所有容器都应定义一个名为size_type 的类型。来自 N3337,23.2.1 通用容器要求 [container.requirements.general]:

表达式:X::size_type
返回类型:无符号整数类型
断言:size_type可以表示任何非-负值 difference_type
复杂性:编译时间

所以你的代码可以简单地看起来像:

typename some_container::size_type variable_x;

【讨论】:

  • 如果some_container 是一个可能包含也可能不包含size_type 的自定义容器会怎样
  • @iammilind:如果自定义类型不满足容器要求,则它们不是容器。例如,它们可能不适用于标准库容器适配器等......换句话说,根据定义,容器是满足容器要求的类型。
  • @iammilind:即使是标准库中一些看起来像容器 (basic_string) 的类型实际上也不是容器,因为它们不满足所有要求(交换使迭代器无效,假定 POD)。
  • 如果提问者使用“容器”一词来表示“满足我自己定义的要求,低于标准容器要求的东西”,那么他有几个选择。最明显的是将size_type 添加到需求中,但如果由于遗留原因无法实现,那么 iammilind 的 SFINAE 方法可以进行测试。这样做的风险(以及一般隐式接口)是,如果size_type 不是some_container 类型的已定义接口的一部分,那么谁能说这些类型之一的实现者不会使用该名称为了不相关的事情?
  • @Veksi:最健壮的方法是为每个本身就是标准容器的杂项类编写一个包装器 (en.wikipedia.org/wiki/Adapter_pattern)。或者至少,它实现了您想要使用的容器要求的子集。这样,您可以在一个地方处理每种类型的非标准性,而不是在使用它们的每个函数中处理。使用这种设计,如果您想编写对多种类型进行操作的模板化包装器,您可以通过检测他们喜欢 iammalind 的答案来实现。另请查看boost::enable_if / std::enable_if
【解决方案4】:

你可以而且你应该使用typename some_container::size_type,即使是大多数STL编译器typedef size_type as size_t,但正如你所说,使用这种技术你可以支持自定义容器

【讨论】:

    【解决方案5】:

    使用@Matthieu M. 的答案作为起点,我使用SFINAE 来确定T::size_type 是否存在,而不是检查size()。非常相似,只是避开了auto

    /** Fails if T::size_type isn't defined. */ 
    template <class T>
    typename T::size_type SizeType(const T &&t);
    
    /** Fallback to a known size. */
    template <class T>
    std::size_t SizeType(const T &t);
    
    ...
    
    using size_type = decltype(SizeType<T>(std::declval<T>()));
    

    与 Matthieu 一样,在这种情况下,方法的参数必须不同,以避免歧义。

    【讨论】:

      猜你喜欢
      • 2010-10-29
      • 2018-02-15
      • 2015-11-21
      • 2018-10-09
      • 2011-01-29
      • 2010-12-06
      • 2012-04-13
      • 2013-06-06
      相关资源
      最近更新 更多