Why alias a template parameter in C++ standard containers? - c++

Reviewing the code of Microsoft's STL (specifically std::vector), I came across the following lines of code (irrelevant code replaced with /* ... */):
// CLASS TEMPLATE vector
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector // varying size array of values
{
/* ... */
public:
/* ... */
using value_type = _Ty;
using allocator_type = _Alloc;
using pointer = typename _Alty_traits::pointer;
using const_pointer = typename _Alty_traits::const_pointer;
using reference = _Ty&;
using const_reference = const _Ty&;
using size_type = typename _Alty_traits::size_type;
using difference_type = typename _Alty_traits::difference_type;
/* ... */
};
I was wondering why the convention of assigning a type alias to a template type is used here?

I was wondering why the convention of assigning a type alias to a template type is used here?
Suppose you have a template function that accept an STL container (std::vector, std::deque, std::set, std::multi_set, ...)
template <typename T>
void foo (T const & t)
{
// ...
}
and that you need the type of contained values.
You can, inside foo(), simply write
using needed_type = typename T::value_type;
and this works for std::vector, std::deque, std::set, std::multi_set, std::array, std::map, std::multi_map, etc.

I was wondering why the convention of assigning a type alias to a
template type is used here?
Because it is
the standard way of doing,
makes less error-prone code,
less typing,
more readable, and
all the above makes life easy!
For instance, let's consider the const_pointer, public template alias of std::vector
using const_pointer = typename _Alty_traits::const_pointer;
At some point, you want to know this type and use in a function, how could have been possible without the above alias?
Of course, you can write
#include <memory> // std::allocator_traits
using const_pointer = typename std::allocator_traits<typename std::vector</*type*/>::allocator_type>::const_pointer;
anywhere in your program. But that tends to more error-prone situations (e.g. missing some typenames and so on), and more typing.
Therefore, it makes sense to collect those kinds of types of a container and provide public-aliases-types.

The C++ standard requires std::vector to provide a double-handful of nested names, and those are the ones the question is asking about. That's why value_type, allocator_type, pointer, etc. are there. You can use those names in your code when you need the types that they refer to. It's not at all uncommon to see, for example, std::vector<int>::iterator in user code to refer to the type of the iterator that std::vector<int> provides.
As to why they're written the way they are, that's a broader consistency issue. Templates throughout the Dinkumware implementation of the C++ standard library (which is what Microsoft ships) use _Ty as the base name for general types. When there are two types you'll see _Ty1 and _Ty2. When there's an internal typedef for a name it will be _Myty (for "My" type). That consistency makes maintaining the code easier.

That convention actually used for providing "traits". Naming for those types follows some convention that allows write functions like distance in terms of those traits and make it working for many containers.
And I'm pretty sure that having those types for standard C++ library is part of specification and they had to adhere it.

Related

What is the purpose of `std::tuple_size<std::array>`

I have two questions concerning the helper class std::tuple_size<std::array> of std::array.
First, there is a constexpr size() member function in class std::array, why does one need std::tuple_size<std::array>?
Second, is the name tuple_size misleading?
The design goal of std::tuple_size (and its friends std::tuple_element and std::get) is to work not only on literal std::tuples, but on anything tuple-like. Something being tuple-like generally just means that it is a compound type containing a fixed number of ordered elements.
You can clearly see this abstraction in use by looking, for example, at how structured bindings are defined : they allow decomposing anything that has sensible implementations of std::tuple_size, std::tuple_element and get<I>.
Under this broadened notion of "tuple-like", it only makes sense that std::array (and std::pair) would specialize these traits to be handled as a tuple of N identical types (resp. a tuple of size 2).
It's part of the tuple interface, which std::array supports. The other parts are std::tuple_element and std::get.
And std::array::size is also redundant, the size is specified as part of the type. That exists as part of the sequence container interface.
std::tuple_size and std::tuple_element and function templates called get provide a generic interface to "list of objects with constant count and types" which can be used in template programming without needing to specify types.
#include <utility>
template <class Tuple>
constexpr std::tuple_element_t<Tuple>& get_last(Tuple& t) {
constexpr auto size = std::tuple_size<Tuple>::value;
static_assert(size > 0);
return get<size-1>(t);
}
This get_last function will work with std::tuple, std::pair, std::array, and any other user types that provide specializations for std::tuple_size, std::tuple_element, and a function template get in a namespace related to the type.
Also, the language itself looks for and uses std::tuple_size, std::tuple_element, and get when the structured binding syntax is used. Again this can be any of the same types, including a user-defined type that implements the interface.
using MyVec = std::array<double, 3>;
void f(const MyVec& v) {
const auto& [x, y, z] = v;
// ...
}

Incompatibility between std::swap boost::unordered_set and std::vector?

something in is messing out with std::swap and boost::unordered_set ?
I am experiencing some problems while using std::swap and boost::unordered_set. The following code generates a C2039 error on VC2017/VC2019. Comment the line #include and it works finely. Does someone has any clue about this problem ?
#include <vector>
#include <boost/unordered/unordered_set.hpp>
typedef boost::unordered_set<size_t> index_list_type;
int main()
{
index_list_type toto;
index_list_type tutu;
std::swap<index_list_type>(toto, tutu);
return 0;
}
MSVC\14.20.27508\include\vector(1702): error C2039: '_Alloc' : is not member of 'boost::unordered::unordered_set,std::equal_to,std::allocator>'
You should just use std::unordered_set, no need for boost.
Boost doesn't have to work with std algorithms. Check here expecially the "all standard containers specialize it in such a way that only a few internal pointers are swapped instead of their entire contents, making them operate in constant time." part.
Sure many things might be cross compatible between boost and std, but if all the tools you need are available in the same namespace, just use that one.
Boost is an entire ecosystem in itself, aimed at minimizing the reliance on the C++ standard library (remember that std::swap is available since C++11 only). As such, there is boost::swap(), which has overloads for all Boost data types, including boost::unordered_set:
// swap
template<typename Value, typename Hash, typename Pred, typename Alloc>
void swap(unordered_set<Value, Hash, Pred, Alloc>&,
unordered_set<Value, Hash, Pred, Alloc>&);
If you use std::swap then you should consider std::unordered_set, and vice versa if you can't or don't want to, then you should stick with Boost all the way. Try to minimize mixing std and boost.
As to the error - this looks like is a bug in the MSVC standard library; the std::swap<vector> implementation is not explicitly restricted to vectors, which is wrong since it fails whenever the type is specified explicitly.
It looks roughly like this:
template<class T>
class _Vb_reference
{
using _Alvbase = typename T::_Alloc;
};
template<class T>
void swap(_Vb_reference<T> _Left, _Vb_reference<T> _Right)
{
}
That only works when the arguments are deduced, as in
std::swap(toto, tutu); // _Vb_reference<T> is non-deduced context
But fails when the type is specified explicitly:
std::swap<index_list_type>(toto, tutu); // instantiation of _Vb_reference<index_list_type> fails = hard error
A better implementation would include some SFINAE to restrict the template to _Vb_reference<T> types.

Why is std::iterator deprecated?

Template class std::iterator is set to be deprecated in C++17. Why so? It has been a handy way to make sure std::iterator_traits works, especially if you can make use of the default template arguments. Is there some other way of doing it in C++17?
From the proposal that suggested its deprecation:
As an aid to writing iterator classes, the original standard library supplied the iterator class template to automate the declaration of the five typedefs expected of every iterator by iterator_traits. This was then used in the library itself, for instance in the specification of std::ostream_iterator:
template <class T, class charT = char, class traits = char_traits<charT> >
class ostream_iterator:
public iterator<output_iterator_tag, void, void, void, void>;
The long sequence of void arguments is much less clear to the reader than simply providing the expected typedefs in the class definition itself, which is the approach taken by the current working draft, following the pattern set in C++14 where we deprecated the derivation throughout the library of functors from unary_function and binary_function.
In addition to the reduced clarity, the iterator template also lays a trap for the unwary, as in typical usage it will be a dependent base class, which means it will not be looking into during name lookup from within the class or its member functions. This leads to surprised users trying to understand why the following simple usage does not work:
#include <iterator>
template <typename T>
struct MyIterator : std::iterator<std::random_access_iterator_tag, T> {
value_type data; // Error: value_type is not found by name lookup
// ... implementations details elided ...
};
The reason of clarity alone was sufficient to persuade the LWG to update the standard library specification to no longer mandate the standard iterator adapators as deriving from std::iterator, so there is no further use of this template within the standard itself. Therefore, it looks like a strong candidate for deprecation.
You can also see STL's reasoning in LWG 2438. (h/t T.C.)
As for some other way of doing it, not really. You could basically implement your own version of std::iterator (which isn't too hard) or manually write out all of those typedefs (which isn't too hard either, and I actually prefer it for clarity).
As Barry states, the working group has decided that explicitly declaring the types in the class is more readable and leads to less surprises than inheriting from std::iterator.
It's not too hard, though, to convert to the explicit types (below example taken from www.fluentcpp.com here). Given a class that was declared like so:
class MyIterator
: public std::iterator<std::forward_iterator_tag, int, int, int*, int&>
{
...
The class without std::iterator becomes:
class MyIterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
// ...

How to Enable a Custom Container for the Scoped Allocator Model

This is a long post, so I would like to write the sole question at the top:
It seems I need to implement "allocator-extended" constructors for a custom container that itself doesn't use an allocator, but propagates this to its internal implementation which is a variant type and whose allowed types may be a container like a std::map, but also a type which doesn't need an allocator, say a boolean.
Alone, I have no idea how to accomplish this.
Help is greatly appreciated! ;)
The "custom container" is a class template value which is an implementation of a representation of a JSON data structure.
Class template value is a thin wrapper around a discriminated union: class template variant (similar like boost variant). The allowed types of this variant represent the JSON types Object, Array, String, Number Boolean and Null.
Class template value has a variadic template template parameter pack Policies which basically defines how the JSON types are implemented. Per default the JSON types are implemented with std::map (for Object), std::vector (for Array), std::string (for JSON data string) and a few custom classes representing the remaining JSON types.
A type-machinery defined in value is used to create the recursive type definitions for the container types in terms of the given Policies and also value itself. (The variant class does not need to use a "recursive wrapper" for the implementation of the JSON containers when it uses std::map or std::vector for example). That is, this type machinery creates the actual types used to represent the JSON types, e.g. a std::vector for Array whose value_type equals value and a std::map for Object whose mapped_type equals value. (Yes, value is actually incomplete at this moment when the types are generated).
The class template value basically looks as this (greatly simplified):
template <template <typename, typename> class... Policies>
class value
{
typedef json::Null null_type;
typedef json::Boolean boolean_type;
typedef typename <typegenerator>::type float_number_type;
typedef typename <typegenerator>::type integral_number_type;
typedef typename <typegenerator>::type string_type;
typedef typename <typegenerator>::type object_type;
typedef typename <typegenerator>::type array_type;
typedef variant<
null_type
, boolean_type
, float_number_type
, integral_number_type
, string_type
, object_type
, array_type
> variant_type;
public:
...
private:
variant_type value_;
};
value implements the usual suspects, e.g. constructors, assignments, accessors, comparators, etc. It also implements forwarding constructors so that a certain implementation type of the variant can be constructed with an argument list.
The typegenerator will basically find the relevant implementation policy and use it unless it doesn't find one, then it uses a default implementation policy (this is not shown in detail here, but please ask if something should be unclear).
For example array_type becomes:
std::vector<value, std::allocator<value>>
and object_type becomes
std::map<std::string, value, std::less<std::string>, std::allocator<std::pair<const std::string, value>>>
So far, this works as intended.
Now, the idea is to enable the user to specify a custom allocator which is used for all allocations and all constructions within the "container", that is value. For example, an arena-allocator.
For that purpose, I've extended the template parameters of value as follows:
template <
typename A = std::allocator<void>,
template <typename, typename> class... Policies
>
class value ...
And also adapted the type machinery in order to use a scoped_allocator_adaptor when appropriate.
Note that template parameter A is not the allocator_type of value - but instead is just used in the type-machinery in order to generate the proper implementation types. That is, there is no embedded allocator_type in value - but it affects the allocator_type of the implementation types.
Now, when using a state-ful custom allocator, this works only half-way. More precisely, it works -- except propagation of the scoped allocator will not happen correctly. E.g.:
Suppose, there is a state-ful custom-allocator with a property id, an integer. It cannot be default-constructed.
typedef test::custom_allocator<void> allocator_t;
typedef json::value<allocator_t> Value;
typedef typename Value::string_type String;
typedef Value::array_type Array;
allocator_t a1(1);
allocator_t a2(2);
// Create an Array using allocator a1:
Array array1(a1);
EXPECT_EQ(a1, array1.get_allocator());
// Create a value whose impl-type is a String which uses allocator a2:
Value v1("abc",a2);
// Insert via copy-ctor:
array1.push_back(v1);
// We expect, array1 used allocator a1 in order to construct internal copy of value v1 (containing a string):
EXPECT_EQ(a1, array1.back().get<String>().get_allocator());
--> FAILS !!
The reasons seems, that the array1 will not propagate it's allocator member (which is a1) through the copy of value v1 to its current imp type, the actual copy of string.
Maybe this can be achieved through "allocator-extended" constructors in value, albeit, it itself does not use allocators - but instead needs to "propagate" them appropriately when needed.
But how can I accomplish this?
Edit: revealing part of the type generation:
A "Policy" is a template template parameter whose first param is the value_type (in this case value), and the second param is an allocator type. The "Policy" defines how a JSON type (e.g. an Array) shall be implemented in terms of the value type and the allocator type.
For example, for a JSON Array:
template <typename Value, typename Allocator>
struct default_array_policy : array_tag
{
private:
typedef Value value_type;
typedef typename Allocator::template rebind<value_type>::other value_type_allocator;
typedef GetScopedAllocator<value_type_allocator> allocator_type;
public:
typedef std::vector<value_type, allocator_type> type;
};
where GetScopedAllocator is defined as:
template <typename Allocator>
using GetScopedAllocator = typename std::conditional<
std::is_empty<Allocator>::value,
Allocator,
std::scoped_allocator_adaptor<Allocator>
>::type;
The logic for deciding whether to pass an allocator to child elements is called uses-allocator construction in the standard, see 20.6.7 [allocator.uses].
There are two standard components which use the uses-allocator protocol: std::tuple and std::scoped_allocator_adaptor, and you can also write user-defined allocators that also it (but it's often easier to just use scoped_allocator_adaptor to add support for the protocol to existing allocators.)
If you're using scoped_allocator_adaptor internally in value then all you should need to do to get scoped allocators to work is ensure value supports uses-allocator construction, which is specified by the std::uses_allocator<value, Alloc> trait. That trait will be automatically true if value::allocator_type is defined and std::is_convertible<value::allocator_type, Alloc> is true. If value::allocator_type doesn't exist you can specialize the trait to be true (this is what std::promise and std::packaged_task do):
namespace std
{
template<typename A, typename... P, typename A2>
struct uses_allocator<value<A, P...>, A2>
: is_convertible<A, A2>
{ };
}
This will mean that when a value is constructed by a type that supports uses-allocator construction it will attempt to pass the allocator to the value constructor, so you do also need to add allocator-extended constructors so it can be passed.
For this to work as you want:
// Insert via copy-ctor:
array1.push_back(v1);
the custom_allocator template must support uses-allocator construction, or you must have wrapped it so that Value::array_type::allocator_type is scoped_allocator_adaptor<custom_allocator<Value>>, I can't tell from your question if that's true or not.
Of course for this to work the standard library implementation has to support scoped allocators, what compiler are you using? I'm only familiar with GCC's status in this area, where GCC 4.7 supports it for std::vector only. For GCC 4.8 I've added support to forward_list too. I hope the remaining containers will all be done for GCC 4.9.
N.B. Your types should also use std::allocator_traits for all allocator-related operations, instead of calling member functions on the allocator type directly.
Yes, value is actually incomplete at this moment when the types are generated
It is undefined behaviour to use incomplete types as template arguments when instantiating standard template components unless speficied otherwise, see 17.6.4.8 [res.on.functions]. It might work with your implementation, but isn't required to.

C++ Function templates with templatized arguments

I am trying to write a function which can take any of the standard containers(list, stack, vector etc) as it's arguments. I also wish to know the type that is within the container. Here is what I have tried.
#include<iostream>
#include<list>
#include<vector>
template<class data_type, template<class> class container_type>
void type(container_type<data_type>& _container){
std::cout<<typeid(container_type).name()<<std::endl;
}
int main(){
std::list<int> list_t;
std::vector<int> vector_t;
type(list_t);
type(vector_t);
}
The type of container_type once inside this function is always _Container_base_aux_alloc_empty which(I think) is a base class of the standard containers.
What is going on here?
How would I make this function return the correct type?
The typeid of container_type is no use, because that's just a template class, and a template class isn't a real type at all, it only becomes one after instantiation. So what you really want is the type of data_type for the value type, and the type of container_type<data_type> for the type of the instantiated container. Even better, of course, is to take container_type<data_type>::value_type as the value type.
Note that most containers take more than one template parameter, so you'd be better off writing this with variadic templates:
template <template <typename...> class Container, typename ...Args>
void print_type(const Container<Args...> &)
{
typedef typename Container<Args...>::value_type value_type;
print(typeid(Container<Args...>).name());
print(typeid(value_type).name());
}
Your code won't work, because as soon as someone swaps out the allocator or something like that, then you're done for. You should take any T and use ::value_type, if in C++03, or type deduction in C++0x.
Also, .name() isn't defined to return anything useful, at all. In any situation. An implementation could return "har har sucker! good luck using this language feature" for every type and be conforming.
I would not trust the output of typeid() that much. type_info::Name is not guaranteed to return some unique identfier. So it might very well be the type inside the function is what you expect.
The best way to get some kind of name for the type is to use a macro, something like this:
template<class data_type, template<class> class container_type>
void type_helper(container_type<data_type>& _container, const char* charStr){
std::cout<< charStr << std::endl
}
#define type(container) type_helper(container, #container)
You already have the type of the container. It's data_type. Just use it like this. If in doubt you can also use typename container_type::value_type which is a typedef for the template argument of the container.
So much for using types. Returning a type is something entirely different in C++ and is generally considered a part of template meta-programming.
This, rather pointless snippet, extracts the value_type from some type T.
template<typename T>
struct inner_type {
typedef T::value_type value_type;
};
But you might as well use the value_type directly and avoid this piece of obfuscation.