3

How do I distinguish template arguments by a typedef?

Situation: I have lists (implemented as vectors) of several types. And I have a special StringList which I want to handle differently.

typedef std::vector<std::string> StringList;

inline void f(StringList const& list) {
  ...
}

template <typenamte T>
inline void f(std::vector<T> const& list) {
  ...
}

In cases where my variables are defined als StringList I would like to have the first version called, when a variable is defined as std::vector<std::string> I would like to have the second version called. But StringList and std::vector<std::string> call the first version. Using using gives the same behaviour.

If that is not possbile, an idea for a reasonable workaround would be nice.

Of course extending std::vector<std::string> would help, but since that is not a good idea at all I don't know how to distinguish them.

Mike M
  • 2,263
  • 3
  • 17
  • 31
  • 4
    std::vector and StringList are same types, you cannot do some overloading here. – ForEveR Jul 30 '13 at 10:05
  • An idea or best practise for a workaround? – Mike M Jul 30 '13 at 10:07
  • First of all, that is not possible, precisely because there is absolutely no difference between `std::vector` and `StringList` in your code. Second, why do have such a scenario where you need to do this? What problem are you trying to solve? – Nawaz Jul 30 '13 at 10:09
  • I have no ideas, WHAT you want to doing. If you want to divide std::vector and std::vector - there is no workarounds. – ForEveR Jul 30 '13 at 10:09
  • I need `std::vector` for several things, just there is a special type `StringList` which happens to be different. How could I model `StringList` without having to pass through all the `std::vector` accessors? – Mike M Jul 30 '13 at 10:10
  • If you want to work differently with vector and vector - it's simply. – ForEveR Jul 30 '13 at 10:22
  • Nope, they should behave the same, only the few `StringList` are different. – Mike M Jul 30 '13 at 10:28
  • On a very fundamental level, you generally want to group data and the functions that work on them in classes. Right now it seems that you are not using object-oriented approach. – Jesse Good Jul 30 '13 at 10:46
  • Well I have logically two different types, which happen to have the same implementation `std::vector`. Since they are logically different I want them to behave different. Where do you see problems with OO in this case? – Mike M Jul 30 '13 at 10:50
  • They should be grouped into separate classes and the behavior (functions) should be members of those classes that manipulate the data. – Jesse Good Jul 30 '13 at 10:57
  • So you suggest wrapping `std::vector` in a class and passing through all member functions of it? That's a lot of code for "just" distinguishing two logical types. – Mike M Jul 30 '13 at 11:10
  • @MikeM: Sometimes OO requires a lot of code. Also, if the class "behaves differently", you'll probably eventually find that you want to implement some of those members differently. – Mooing Duck Aug 06 '13 at 23:57

2 Answers2

4

You have at least three options:

#include <vector>
#include <string>

// option 1

struct StringList : std::vector<std::string>
{
    // optionally forward the constructors of std::vector that are called
};

// option 2

struct String : std::string
{
    // optionally forward the constructors of std::string that are called
};

typedef std::vector<String> StringList;

// option 3

struct StringTag
{
};

typedef std::allocator<StringTag> StringAllocator;

typedef std::vector<std::string, StringAllocator> StringList;

The first and second options both require forwarding some of the constructors of the base class. The second option is probably better, based on the assumption that you probably only need to forward the std::string copy-constructor when adding strings to a string list.

The third option is my favourite - partly because it's a sneaky hack, but also because it requires no inheritance or forwarding. It works because the T argument of the allocator template-argument in a std container is never used.

EDIT: the C++ standard implies that the allocator's value_type must match the value_type of the container - so you can only use the third option if your standard library implementation allows it. If not, I would recommend the first or second options.

EDIT 2: See also: Is it wrong if the standard container element type and std::allocator type are different?

Community
  • 1
  • 1
willj
  • 2,991
  • 12
  • 24
  • Your third option looks really interesting, because it would preserve the interface without any work. You say it's a hack. Is it reliable in the sense, that it will work in the future? – Mike M Jul 30 '13 at 12:10
  • I can't find anything in the C++ standard that explicitly prevents me from writing `std::vector >`. All the implementations I've used seem to allow this too. Having said that, it's probably not something that is "intended" to be allowed, and probably should be banned ;). – willj Jul 30 '13 at 12:36
  • At least one implementation explicitly disallows it: http://stdcxx.apache.org/doc/stdlibug/15-2.html – willj Jul 30 '13 at 12:42
  • And in fact, the standard implies that it is a requirement for the allocator's `value_type` to match the `value_type` of the container. – willj Jul 30 '13 at 12:48
  • Ok, so I'll better not use it. Thank you for your efforts. – Mike M Jul 30 '13 at 14:20
1

Okay, here is the quick and dirty solution to your problem:

struct StringList : std::vector<std::string>
{};

Now your functions will be called correctly. I don't recommend it, but without anymore information it is difficult to give a better answer. Also, please read Is there any real risk to deriving from the C++ STL containers? to understand the risk and problems involved. But, also like I said in the comments, I would recommend wrapping them in classes instead.

Community
  • 1
  • 1
Jesse Good
  • 50,901
  • 14
  • 124
  • 166