Standard Library Containers with Additional Optional Template Parameters

Standard Library Containers with additional optional template parameters?

I found the following issue report, which says

There is no ambiguity; the standard is clear as written. Library implementors are not permitted to add template parameters to standard library classes. This does not fall under the "as if" rule, so it would be permitted only if the standard gave explicit license for implementors to do this. This would require a change in the standard.

The LWG decided against making this change, because it would break user code involving template template parameters or specializations of standard library class templates.

The books and people that say an implementation may add other optional parameters seem to be wrong.

template classes as template parameters

Why not just pass the container type as template parameter, and find out the element type from it? In your example code you don't even need the element type:

template <typename C>
ostream&
operator<<
(ostream& p_os,const C& p_c)
{
typedef typename C::value_type element_type; // if needed
for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
{
p_os.operator<<(*cit);
}
return p_os;
}

(Although it might be unwise to use this for global functions like this without some enable_if trickery, since it will otherwise match any argument.)

EDIT: You could for example attempt to restrict this to classes with a nested value_type (which all containers have):

template <typename C, typename T = typename C::value_type>
ostream&
operator<<
(ostream& p_os,const C& p_c)

Pass a container type as the typename of a template in c++

With minimum changes to make it work, your code could be this:

#include <array>

template <typename T>
int find_size(const T& t)
{
return (t.size());
}

int main(void)
{
std::array<int, 10> test;
for (int i = 0; i < 10; i++)
{
test[i] = i;
}
find_size(test);
}

basically I want my function to be able to take an abritary type of container

Thats exactly what the above does. It works for any container type T that has a size().


If you actually want to parametrize find_size on a template rather than a type, then you can use a template template parameter:

#include <array>

template <template<class,std::size_t> class C>
int find_size(const C<int,10>& t)
{
return (t.size());
}

int main(void)
{
std::array<int, 10> test;
for (int i = 0; i < 10; i++)
{
test[i] = i;
}
find_size<std::array>(test);
}

However, using this is either more complicated than illustrated here, or of more limited use than the above: For the function parameter you need a type not just a template, and this find_size will only work with a template C that has 2 parameters, one type and one non-type parameter of type size_t (and I am actually not aware of any other container but std::array with that template parameters).


TL;DR: This is not a use case where a template template parameter is needed.

Is it possible to pass the template of a container to the template of a function?

Yes, but you don't need it here.

STL container as template parameter in function, error in call

std::vector isn't a template of one parameter, it takes an allocator type as well. You can use it as vector<T> simply because the second parameter has a default (std::allocator<T>).

As it's written, your template function cannot accept any standard container, since off the top of my head, none take just a single type parameter.

An approach that would work, and not require you to know how many template parameters a container requires, is to accept a container type (not template), and glean the value type from the container type.

template<class Seq>
void show_sequence(Seq const& sequence)
{
typedef typename Seq::value_type T;
for_each(sequence.begin(), sequence.end(), show_element<T>);
}

All standard containers have a value_type member, so this will work with any of them. Furthermore, it will work with any container that takes its cue from the standard library.

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

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.

Optional Template parameter

You can have default template arguments, which are sufficient for your purposes:

template<class T, class U = T, class V = U>
class Test
{ };

Now the following work:

Test<int> a;           // Test<int, int, int>
Test<double, float> b; // Test<double, float, float>

Default values in templates with template arguments ( C++ )

Perhaps you'd prefer this:

#include <vector>
#include <list>

using namespace std;

template <class Container>
class ForExamplePurposes {
typedef typename Container::value_type T;
Container items;
public:
};

int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
}

This uses "static duck typing". It is also a bit more flexible as it doesn't force the Container type to support STL's Allocator concept.


Perhaps using the type traits idiom can give you a way out:

#include <vector>
#include <list>

using namespace std;

struct MyFunkyContainer
{
typedef int funky_type;
// ... rest of custom container declaration
};

// General case assumes STL-compatible container
template <class Container>
struct ValueTypeOf
{
typedef typename Container::value_type type;
};

// Specialization for MyFunkyContainer
template <>
struct ValueTypeOf<MyFunkyContainer>
{
typedef MyFunkyContainer::funky_type type;
};

template <class Container>
class ForExamplePurposes {
typedef typename ValueTypeOf<Container>::type T;
Container items;
public:
};

int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
ForExamplePurposes< MyFunkyContainer > c;
}

Someone who wants to use ForExamplePurposes with a non-STL-compliant container would need to specialize the ValueTypeOf traits class.

C++ standard container and STL container in c++

Nothing in the C++ Standard Library "belongs" to the STL. STL is a different library that only influenced many parts in the C++ Standard Library. From the tag wiki:

[STL] is a C++ library of generic containers, iterators, algorithms,
and function objects. When C++ was standardised, large parts of the
STL were adopted into the Standard Library
, […]

However, many people refer to the C++ Standard Library as the Standard Template Library, which is not entirely correct. I'm guessing that if you're not allowed to use the STL, they actually mean that you're not allowed to use the C++ Standard Library. But you'd have to ask them to know what they really mean.

For more information, see What's the difference between "STL" and "C++ Standard Library"?



Related Topics



Leave a reply



Submit