0

I would like to make some template functions work with existing template-struct helpers. However template argument deduction fails. Is there a work-around?

Example

This overloaded operator << compiles and works:

template <typename T>
inline typename std::vector<T>&
operator<<(
    typename std::vector<T>& vec,
    const typename std::vector<T>::value_type& val)
{
    vec.push_back(val);
    return vec;
}

But when I try to use a helper struct this doesn't compile:

template<typename T>
struct Vector
{
    typedef std::vector<T> Type;
};

template <typename T>
inline typename Vector<T>::Type&
operator<<(
    typename Vector<T>::Type& vec,
    const typename Vector<T>::Type::value_type& val)
{
    vec.push_back(val);
    return vec;
}

gcc error:

error: no match for 'operator<<' (operand types are 'std::vector<int>' and 'int')
    ...
note: candidate: 
'template<class T> typename Vector<T>::Type& operator<<
(typename Vector<T>::Type&, const typename Vector<T>::Type::value_type&)'
 operator<<(
 ^~~~~~~~
note:   template argument deduction/substitution failed:
note:   couldn't deduce template parameter 'T'

clang error:

error: invalid operands to binary expression ('std::vector<int>' and 'int')
   vec << int(2);
   ~~~ ^  ~~~~~~
note: candidate template ignored: couldn't infer template argument 'T'
operator<<(
^

Live example

Question

  • What prevents a successful template parameter deduction in this case?
  • Is there a c++03 work-around for this case? Alias templates would solve the problem in c++11.

Note: In my real problem second parameter is not necessarily T and I can't use it to deduce vector type.

Note 2: the real helper struct contains some platform specific pre-processing and looks something like:

template <class T>
struct Helper
{
#if defined(_WIN32_WCE)
    typedef std::vector<T, WMHeapAllocator<T> > Vector;  
#else
    typedef std::vector<T> Vector;      
#endif
};
Community
  • 1
  • 1
AMA
  • 4,114
  • 18
  • 32
  • 2
    in `C::Type`, `T` is non deducible (we might have, (in generic case) several `T` which match). (which is also the case in first snippet with `::value_type` so `T` is only deduced from first parameter). – Jarod42 Feb 02 '18 at 09:42

1 Answers1

2

This is a Non-deduced contexts, which is not restricted to C++03. See my previous answer Template parameter cannot be deduced.

As for a workaround, you need to make one of the argument in the function where T can be deduced. Once it is deduced from one place, compiler will use it in other places.

In your case, if you can be sure that value_type will be T, then use this will work:

template <typename T>
inline typename Vector<T>::Type&
operator<<(
    typename Vector<T>::Type& vec,
    const T& val)
{
    vec.push_back(val);
    return vec;
}

Here T is deduced from second argument, and used in the first.

EDIT( to reflect the edit of question )

You don't need a helper class, a template template solution might be better:

template<template<typename, typename> class Container, class T, class U>
inline Container<T, U>&
operator<<(
        Container<T, U>& vec,
        const typename Container<T, U>::value_type& val)
{
    vec.push_back(val);
    return vec;
}
llllllllll
  • 16,169
  • 4
  • 31
  • 54
  • Unfortunately in my real problem the second parameter is not necessarily connected to `T`. I've updated the answer. – AMA Feb 02 '18 at 09:59
  • Maybe you can try a template template parameter, instead of a helper class. – llllllllll Feb 02 '18 at 10:12
  • Aha, that's clever. So do you suggest doing something like [this](https://wandbox.org/permlink/06MMOBikTGkD6ys3)? – AMA Feb 02 '18 at 10:37
  • See my updated answer. In fact you don't need this helper class if its only purpose is to store compile time information of the inner type. Your code won't compile, because it's still a non-deduced context. – llllllllll Feb 02 '18 at 10:42
  • [But this does compile](https://wandbox.org/permlink/VNq12lahthicLGKV). A agree that I don't need the helper in the template function in that case. – AMA Feb 02 '18 at 10:48
  • This is the explicit `std::vector` version being called, not the template template parameter version. If you remove the `std::vector` version of `<<`, it will fail. – llllllllll Feb 02 '18 at 10:50
  • Yes, we speak about slightly different things :) In my case full specialization for `std::vector` solves the problem and will work with the existing helpers (see Note 2 in the question). I also think that it is easier to read. – AMA Feb 02 '18 at 11:03