1

This code will fail with error message (line numbers are off). How do I fix this (keeping same intent)?

g++ -o c_test c_test.cpp

c_test.cpp: In function 'int main(int, char**)':

c_test.cpp:28:18: error: no matching function for call to 'wcalc(CWrapped<5>::u_type&)'

c_test.cpp:28:18: note: candidate is:

c_test.cpp:17:58: note: template int wcalc(typename CWrapped::u_type)

The wrapped type is passed to both "calc" and "wcalc" function, but the 2nd one fails. I want to be able to wrap the types so I can use a compile-time define to specify different types but still use the same wrapped function

// Example template class
template <int T_B>
class m_int {
public:
  int x;
  m_int() { x = T_B; }
  int to_int() { return(x); }
};

// Desired Typedef wrap
template <int T_BITS> struct CWrapped {
  typedef m_int<T_BITS> u_type;
};


// This is ok, no wrapping
template <int T_BITS> int calc(m_int<T_BITS> x) {
  return(x.to_int());
}
// This fails when instantiated
template <int T> int wcalc(typename CWrapped<T>::u_type x) {
  return(x.to_int());
}


int main(int argc, char* argv[]) {
  CWrapped<5>::u_type s;

  int x = calc(s);
  int y = wcalc(s);
  return(0);
}
user4221
  • 89
  • 8

2 Answers2

3

From the C++11 Standard, paragraph 14.8.2.5/16

"If, in the declaration of a function template with a non-type template parameter, the non-type template parameter is used in a subexpression in the function parameter list, the expression is a non-deduced context as specified above. Example:"

template <int i> class A { / ... / };
template <int i> void g(A<i+1>);
template <int i> void f(A<i>, A<i+1>);
void k() {
    A<1> a1;
    A<2> a2;
    g(a1); // error: deduction fails for expression i+1
    g<0>(a1); // OK
    f(a1, a2); // OK
}

"Note: Template parameters do not participate in template argument deduction if they are used only in non-deduced contexts. For example:"

template<int i, typename T> 
T deduce(typename A<T>::X x, // T is not deduced hereT t, // but T is deduced here
typename B<i>::Y y); // i is not deduced here
A<int> a;
B<77> b;
int x = deduce<77>(a.xm, 62, b.ym);
// T is deduced to be int, a.xm must be convertible to
// A<int>::X
// i is explicitly specified to be 77, b.ym must be convertible
// to B<77>::Y

Because of the above, your non-type template parameter T cannot be deduced: you have to provided it explicitly:

int main(int argc, char* argv[]) {
  CWrapped<5>::u_type s;

  int x = calc(s);
  int y = wcalc<5>(s); // Template argument 5 cannot be deduced!
  return(0);
}

Also see this related link: C++, template argument can not be deduced (courtesy of @NateKohl)

Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • thanks, I don't see exactly how my example compares. If compiler was able to deduce "5" for calc, why not also for "wcalc". Not sure it qualifies as a subexpression as it's merely substitution. Unfortunately specifying the '5' explicitly defeats the purpose so I need to think of another way. Thanks again – user4221 Jan 08 '13 at 23:47
  • @user451596: it does not work for `wcalc` because type deduction is not performed on dependent type names (i.e. type names expressed as a function of a template argument, in your case `T`). this is what the second part of the quote above from the Standard wants to make you aware of. the reason why it is not done is best explained in the link I posted at the end of my answer, I guess – Andy Prowl Jan 08 '13 at 23:50
  • @user451596: in short: for a parameter like `f(A a)`, the type of `a` can be deduced, but for a parameter like `f(typename A::type` a) it cannot, because there may be many `T`s which turn out to result in the same `A::type` and the choice would be ambiguous (even supposing the compiler could go and look for all the possible `T`s) – Andy Prowl Jan 08 '13 at 23:52
  • Thanks Andy, yes, I saw the link and now follow both your explanation and the given one – user4221 Jan 09 '13 at 01:30
  • @user451596: if you think i gave a satisfactory answer to your question, please consider marking the answer as accepted. cheers – Andy Prowl Jan 09 '13 at 02:09
0

Your problem is that CWrapped is a map from int to u_type in a dependent context.

You need a map from u_type to int in order for type deduction to work.

Template type deduction is simple pattern matching, it doesn't invert arbitrary template constructs.

As an example, here is how you can extract which CWrapped would resolve to T:

template<typename T>
struct GetWrapped;

template<int N>
struct GetWrapped< m_int<N> > {
  typedef CWrapped< N > type;
};

template <int T> int wcalc(T const& x) {
  typedef typename GetWrapped<T>::type CWrappedType; // assuming you need it for something
  return(x.to_int());
}

and here is how you can only accept T for which there is such a mapping at overload resolution:

#include <type_traits>
template <int T> auto wcalc(T const& x)->typename std::enable_if< sizeof(typename GetWrapped<T>::type), int>::type  {
  return(x.to_int());
}

which uses SFINAE and C++11 features to check that GetWrapped<T> is a valid construct.

Now, this is probably an XY question, as in you asked X but you really needed to know Y. I'm hoping one of the above will help.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524