Veksi
Veksi

Reputation: 3758

Choosing between size_t and container::size_type in compile time

I was thinking to be more pedantic in choosing data type in a block of code where I would need to choose between size_type size_t in general or container::size_type for container types. My problem is that if I have the following block of code, I don't know how to do it. Can anybody there help?

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...
}

In this case some_container may be a custom container and does not provide size() function. What led me to this thinking was reading the difference between size_t and container::size_type at size_t vs container::size_type. I also read Determine if a type is an STL container at compile time, but the approach feels a bit heavy-handed for my situation.

Upvotes: 2

Views: 1493

Answers (5)

greg
greg

Reputation: 4953

Using @Matthieu M.'s answer as a starting point, I use SFINAE to determine if T::size_type exists, rather than checking for size(). Very similar, just avoids the 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>()));

Like Matthieu did as well, the parameters to the methods must be different in this case to avoid ambiguity.

Upvotes: 0

Matthieu M.
Matthieu M.

Reputation: 299810

You are on the right way using decltype, the trick is to use SFINAE which is easily done using either template classes or functions overloads. I will show the function way since it's so easy in 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>());

Usage:

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

Note: the switch and sugar coating layer could be blended together, however I thought you might appreciate seeing the steps separately.

Upvotes: 5

iammilind
iammilind

Reputation: 69988

Following is one way to determine if a class contains a type (e.g. size_type) or not:

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

And following is the way to choose between 2 types:

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

Edit start: Here is another simpler approach:

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

Edit end.

So finally, use as below:

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

So now type is either size_type or some_container::size_type, if it has that.

Upvotes: 4

Yakov Galka
Yakov Galka

Reputation: 72479

If it does not have a size_type then it is not a container. Period.

The standard requires that all containers shall define a type named size_type. From N3337, 23.2.1 General container requirements [container.requirements.general]:

Expression: X::size_type
Return type: unsigned integer type
Assertion: size_type can represent any non-negative value of difference_type
Complexity: compile time

So your code can look simply as:

typename some_container::size_type variable_x;

Upvotes: 2

BigBoss
BigBoss

Reputation: 6914

You can and you should use typename some_container::size_type for this, even most STL compilers typedef size_type as size_t but as you say using this technique you can support custom containers

Upvotes: 0

Related Questions