0

I am new to c++ templates. I am writing a set of functions that can manipulate two different template classes. One needs to always be passed by value, the other must be passed by reference because it represents a huge amount of data.

Here is a simplified example. If the arg is tagged a ref type, I want the function signature to be defined as taking it by const ref.

template<bool B, typename, typename T2>
struct if_ {};

template<typename T1, typename T2>
struct if_<true, T1, T2> {
    typedef T1 type;
};

template<typename T1, typename T2>
struct if_<false, T1, T2> {
    typedef T2 type;
};

struct ByvalTypeTag {};
template<typename T>
class Byval : public ByvalTypeTag 
{
    T somedata;
};

struct ByrefTypeTag {};
template<typename T>
class Byref : public ByrefTypeTag
{
    T somedata;
};

template<typename T>
void myfunc(typename if_<std::is_base_of<ByrefTypeTag, T>::value, const T&, T>::type arg)
{

}

int _tmain(int argc, _TCHAR* argv[])
{
    Byref<int> arg;
    myfunc( arg );
    return 0;
}

The error I get is:

error C2783: 'void myfunc(if_::value,const T&,T>::type)' : could not deduce template argument for 'T'

Maybe this is the wrong way of going about it. If possible I am trying to cut down on the number of relatively duplicate templates I am writing for the same function.

Ed Connell
  • 53
  • 5
  • The compiler won't be able to infer `T` from the argument to `myfunc`, the expression is too complicated (and infering types backward through template instantiations could require solving the halting problem, in general)...you have to provide the type explicitly as `myfunc< Byref >(arg)` (or maybe someone can find some other tricky way to do what you want, but that's the most direct solution) – Stephen Lin Mar 15 '13 at 23:30

3 Answers3

1

Yeah, compiler cannot deduce the type. You'll need to provide it with T yourself:

myfunc<ByRef<int>>(arg);

Alternatively you can use the more usual tag-dispatching system:

class byref
{
    typedef byref_tag tag;
    ...
};

template < typename T >
void fun(T const& t, byref_tag) { ... }

template < typename T >
void fun(T t, byval_tag) { ... }

template < typename T >
void fun(T const& t)
{
    typedef typename T::tag tag;
    fun(t,tag());
}

Yet another alternative would involve a wrapper function and class template. Either way though the outer function has to be by ref.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
1

You are trying to deduce type T from a non-deduced context.

The presence of the double colon in the parameter's dependent type usually gives you hint that you won't be able to deduce the type of that parameter (unless you have other deduced contexts in the same function call that help, but here you have just one argument).

From Paragraph 14.8.2.5/5 of the C++11 Standard:

The non-deduced contexts are:

— The nested-name-specifier of a type that was specified using a qualified-id.

[...]

If you need a concrete example, this Q&A on StackOverflow provides a pretty good one. In this case, you could provide the type argument explicitly:

myfunc<Byref<int>>(arg);

Alternatively, you can choose one of the two workarounds proposed in this second Q&A.

Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • So it sounds like a template function definition can't sense the type and add in the const & automatically. I don't want the user to have to manually select the template. – Ed Connell Mar 16 '13 at 04:32
0

You can forward the parameter with this implementation.

template<typename T>
void myfunc_impl(T arg)
{
    // Do the work here.
}

template<typename T>
void myfunc(const T &arg, typename std::enable_if<std::is_base_of<ByrefTypeTag, T>::value>::type* = 0)
{
  myfunc_impl<const T&>( arg ); // Pass a const ref
}

template<typename T>
void myfunc(const T &arg, typename std::enable_if<std::is_base_of<ByvalTypeTag, T>::value>::type* = 0)
{
  myfunc_impl<T>( arg );        // Pass a copy
}
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • I had something similar to this, but I was trying to do away with all the duplicate code. For a binary operator function that could take either type in either position, it ends up requiring all of the combinations. Kind of ugly, so I was hoping for something cleaner. – Ed Connell Mar 16 '13 at 04:28