1

I have encounter serious template type deduction problem when I use method pointer in argument of a template function.

Let's take the following code:

template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
                void (ClassT::*fctPtr)(Arg1T),
                const Arg1T & arg1)
{

}


class TestClass
{

public:
  void testMethodIntArg(int arg)
  {}

  void testMethodDoubleArg(double arg)
  {}

  void testMethodStringArg(const char * arg);

};



int main()
{
  TestClass testClass;

  testTemplateFct(&testClass,
          &TestClass::testMethodIntArg,
          10);

  testTemplateFct(&testClass,
          &TestClass::testMethodDoubleArg,
          10.0);

  /// BEGINNING OF MY PROBLEM
  testTemplateFct(&testClass,
          &TestClass::testMethodStringArg,
          "a string...");
  /// END OF MY PROBLEM

  return 0;
}

If I compile it using g++, I get the following error message:

$ g++ ArgumentDeduction.cpp -o ArgumentDeduction
ArgumentDeduction.cpp: In function ‘int main()’:
ArgumentDeduction.cpp:42:18: error: no matching function for call to ‘testTemplateFct(TestClass*, void (TestClass::*)(const char*), const char [12])’
     "a string...");
                  ^
ArgumentDeduction.cpp:4:13: note: candidate: template<class ClassT, class Arg1T> void testTemplateFct(ClassT*, void (ClassT::*)(Arg1T), const Arg1T&)
 inline void testTemplateFct(ClassT * clazz,
             ^~~~~~~~~~~~~~~
ArgumentDeduction.cpp:4:13: note:   template argument deduction/substitution failed:
ArgumentDeduction.cpp:42:18: note:   deduced conflicting types for parameter ‘const Arg1T’ (‘const char*’ and ‘char [12]’)
     "a string...");

If I remove the reference of the third argument of method testTemplateFct the problem disappears (HOWEVER I ABSOLUTELY NEED THE REFERENCE IN ORDER TO AVOID COPY)

template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
                void (ClassT::*fctPtr)(Arg1T),
                const Arg1T  arg1)
{}

I understand more or less the error message but I do not understand why there is an ambiguity between const char* and char [12]. I do not understand why the problem disappears when I remove the reference.

Finally, I would strongly appreciate any help in order to correct this code while keeping the reference

PS: I know that I can "force" the type deduction by doing:

testTemplateFct(&testClass,
                &TestClass::testMethodStringArg,
                (const char *) "a string...");

but I don't like it very much

Deduplicator
  • 41,806
  • 6
  • 61
  • 104
Philippe MESMEUR
  • 537
  • 4
  • 15

3 Answers3

2

I do not understand why there is an ambiguity between const char* and char [12].

Note that "a string..." is an array with type const char[12]. For the function template testTemplateFct, the parameter arg1 is declared as a reference, i.e. const Arg1T &, then array-to-pointer decay won't occur in template argument deduction and Arg1T is deduced as char[12], which doesn't match the deduced type of Arg1T from the 2nd argument, i.e. const char*, so deduction failed.

I do not understand why the problem disappears when I remove the reference.

When the parameter is declared as pass-by-value array-to-pointer decay is applied; then both the deduced type of Arg1T from the 2nd and 3rd argument will be const char* and everything work fine.

songyuanyao
  • 147,421
  • 15
  • 261
  • 354
2

Your template requires that both the occurrences of Arg1T are deduced to the same type. I believe that is not what you want. Instead the types should be deduced independently:

template <class ClassT, typename Arg1T, typename GivenT>
inline void testTemplateFct(ClassT * clazz,
                void (ClassT::*fctPtr)(Arg1T),
                GivenT &&arg1)
{
    //example use
    (clazz->*fctPtr)(std::forward<GivenT>(arg1));
}
nwp
  • 8,897
  • 2
  • 32
  • 67
1

You have two basic options.

The first one is to change your invocation to:

testTemplateFct(&testClass,
          &TestClass::testMethodStringArg,
          (const char *)"a string...");

The second option is to add an overload:

template <class ClassT, size_t n>
inline void testTemplateFct(ClassT * clazz,
                void (ClassT::*fctPtr)(const char *),
                const char (&arg1)[n])
{
    testTemplateFct<ClassT, const char *>(clazz, fctPtr, arg1);
}

Pick which one works best for you.

A literal character string is actually a const char[n], and not a const char *. The const char array decays to a const char * in an ordinary function call; but this decay does not occur as part of template deduction; hence the problem.

Community
  • 1
  • 1
Sam Varshavchik
  • 84,126
  • 5
  • 57
  • 106