6

More templated woes... I love C++, but sometimes I hate it.

I cannot figure out why the compiler is complaining here, and what I can do about it.

struct blah
{
   template<class t>
   blah(void(*)(t), t){}
};

void Func(int i) {}
void Func2(int& i) {}

void test()
{
   int i = 3;
   blah b(Func, i);   
   blah b2(Func2, i);        //error C2660: 'blah::blah' : function does not take 2 arguments
   blah b3(Func2, (int&)i);  //error C2660: 'blah::blah' : function does not take 2 arguments

}

What is going on here?

I am using MSVC2008.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Tyson Jacobs
  • 352
  • 1
  • 9

3 Answers3

7

The other answers explain what's going on: when template argument deduction finds two ways to deduce a template argument, it looks at each alone and they all must agree exactly.

You can probably get this class working the way you intended by making sure the second use of t is in a "non-deduced context":

template<typename T>
struct identity { typedef T type; };

struct blah
{
   template<class t>
   blah(void(*)(t), typename identity<t>::type){}
};

This way when the blah constructor is called, C++ will deduce t from the function pointer, but won't try to deduce it from the second argument. The type deduced then gets substituted in both places.

aschepler
  • 70,891
  • 9
  • 107
  • 161
3

If you compile the same snippet on gcc, you get a more intuitive error message:

test.cpp:14:20: note:   deduced conflicting types for parameter ‘t’ (‘int&’ and ‘int’)

The compiler is deducing that the template parameter t is int& for the first parameter, and int for the second one. Since these are different types, you get a compilation error.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • But it doesn't help even if I explicitly cast it (example edited) – Tyson Jacobs Jun 28 '13 at 17:10
  • "_Therefore, even_ (...)" -1 There is no expression of type `int&`, period. Nothing is removed. – curiousguy Jun 29 '13 at 22:22
  • @curiousguy so you're saying that `(int&)i` is not of type `int&`? – mfontanini Jun 29 '13 at 22:32
  • @mfontanini That is what I am saying. The cast does nothing. – curiousguy Jun 29 '13 at 22:48
  • 1
    @curiousguy of course that the cast does nothing. I still don't get what you mean. Could you explain [this](http://ideone.com/YxHN9k)? – mfontanini Jun 29 '13 at 22:50
  • 1
    @curiousguy yes, the cast does nothing. Did I say that the cast modified something? OP used a cast, that's why I mentioned that casting does not alter template parameter deduction, it's exactly the same. So we know that `(int&)x` and `(x)` are of type `int&`, right? Then what do you mean with "There is no expression of type `int&`? – mfontanini Jun 29 '13 at 23:36
  • @mfontanini No expression has type `int&`. – curiousguy Jun 29 '13 at 23:42
  • @curiousguy ok, I see. No expression has type `int&`. Did I, at some point in my answer, imply otherwise? – mfontanini Jun 29 '13 at 23:53
  • @mfontanini You imply that "top level reference qualifiers are removed" is relevant and that casting to `int&` is relevant. Neither is. – curiousguy Jun 30 '13 at 00:25
3

In MSVC 2012 Intellisense says (roughly transleted):

1 IntelliSense: No instances of constructor ""blah::blah"" is matching the list of arguments.

Argument types are: (void (int &i), int)

@mfontanini is correct and you have a deduction problem here.

You could add a second constructor

template<class t>
blah(void(*)(t), t){}
template<class t>
blah(void(*)(t&), t&){}
Community
  • 1
  • 1
Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • Unfortunately that is not an option in my case. I am working on a callback wrapper, which can take arbitrary functions with arbitrary parameters, and I don't want to deal with the combinatorial explosion of possible reference arguments. I guess I will have to use static "make" functions instead of constructors. – Tyson Jacobs Jun 28 '13 at 17:28