views:

70

answers:

3

Once again, I wish C++ had stronger typedefs:

#include <vector>

template<typename T>
struct A {
    typedef std::vector<T> List;
};

template<typename T>
void processList(typename A<T>::List list) {
    // ...
}

int main() {
    A<int>::List list;
    processList<int>(list); // This works.
    processList(list);      // This doesn't.
}

Apparently, the compiler sees list as a std::vector<int> and not an A<int>::List, so it can't match it against the A<T>::List that is expected.

In the actual case, it's a longer type name, often repeated, and it's a nuisance. Apart from letting processList accept a vector instead, is there any way to make template type inference work for me?

+4  A: 

is there any way to make template type inference work for me?

No, this is what's called a non-deducible context.

However, why do you need this anyway? The idiomatic way to pass sequences around is by iterator:

template<typename It>
void processList(It begin, It end) {
    typedef typename std::iterator_traits<It>::value_type T;
    // ....
}

int main() {
    A<int>::List list;
    processList(list.begin(), list.end()); // works now
    return 0;
}

(Note that in your question, you pass a vector by value, which is a bad thing to do. For iterators, that's fine and even to be preferred.)

However, if you're really desperate, you can have the function deduce any container taking a certain number of template arguments:

template<typename T, typename A, template<typename,typename> C>
void processList(C<T,A>& cont) {
    // ....
}

Note, however, that this would match any template with two template arguments. OTOH, it wouldn't match a container like std::map, which has a different number of arguments.

sbi
Hmm, good point. I guess I don't care for "idiomatic" that much, because this is for internal use inside one class only. Passing a (reference to... which I left out in this simplified example) the thing itself makes for shorter code at the call site.
Thomas
I passed the vector by value for simplicity of the example. In practice, I pass a const reference, of course. Your update is neat; not that I'll use it (I'm not that desperate) but it's good to keep in mind.
Thomas
+1 but beware (for the edit) that standard containers don't have a fixed number of arguments --it is implementation defined, the implementation just needs to provide defaults for any extra arguments to the templates so that instantiations that assumes the signatures present in the standard compile.
David Rodríguez - dribeas
A: 

Got it. Use inheritance to create a type that is actually different:

template<typename T>
class A : public std::vector<T> {
};

Good thing I don't need the non-default constructors in this case.

Thomas
good solution - no idea why you were down-voted
Elemental
Because it's horrible: `std::vector` does not have a virtual destructor.
Matthieu M.
Well he probably won't use it polymorphically through a base class pointer so it's probably not a problem. Thomas, maybe you can post the final code so we can see if it could pose other problems.
n1ck
I went with sbi's solution after all, passing iterators around. Works like a charm.
Thomas
A: 

There is a simple solution in your case:

template <class C>
void processList(C const& list)
{
  typedef typename C::value_type value_type;
  BOOST_STATIC_ASSERT_MSG((boost::same_type< C, typename A<value_type>::List >),
    NOT_A_A_LIST_TYPE,
    (C, A<value_type>)
  );
  // do your stuff
}

Note that C++0x is coming!

template <typename second>
using TypedefName = SomeType<OtherType, second, 5>;

I wonder if this would work

template <typename value>
using AList = typename A<value>::List;
Matthieu M.