Why does the C ++ template argument fail in this case?

I am trying to write my own delegate system as a substitute for boost :: functions, since the latter makes a lot of the heap allocations that I have profiled to be problematic.
I wrote this as a replacement (a simplified, actual thing uses combined memory and a new place, but it's simple enough to reproduce the error):

template<class A, class B> struct DelegateFunctor : public MyFunctor { DelegateFunctor(void (*fptr)(A, B), A arg1, B arg2) : fp(fptr), a1(arg1), a2(arg2) {} virtual void operator()() { fp(a1, a2); } void (*fp)(A, B); // Stores the function pointer. const A a1; const B a2; // Stores the arguments. }; 

and this helper function:

 template<class A, class B> MyFunctor* makeFunctor(void (*f)(A,B), A arg1, B arg2) { return new DelegateFunctor<A,B>(f, arg1, arg2); } 

It’s strange here:

 void bar1(int a, int b) { // do something } void bar2(int& a, const int& b) { // do domething } int main() { int a = 0; int b = 1; // A: Desired syntax and compiles. MyFunctor* df1 = makeFunctor(&bar1, 1, 2); // B: Desired syntax but does not compile: MyFunctor* df2 = makeFunctor(&bar2, a, b); // C: Not even this: MyFunctor* df3 = makeFunctor(&bar2, (int&)a, (const int&)b); // D: Compiles but I have to specify the whole damn thing: MyFunctor* df4 = makeFunctor<int&, const int&>(&bar2, a, b); } 

Compiler error for version C (B is similar):

 error: no matching function for call to 'makeFunctor(void (*)(int&, const int&), int&, const int&)' 

which is strange, because the compiler actually correctly inferred types in its error message.

Is there a way to get compilation of version B? How to boost :: bind get around this limitation?
I am using GCC 4.2.1. There are no solutions in C ++ 11.

+4
source share
2 answers

The output of arguments cancels the links. By signing the signature of the pointer to function A , we want to get int & , but by matching the actual argument, we want int , and therefore the output is not executed.

One solution is to make the second type non-deducible, for example:

 #include <type_traits> template <typename R, typename A, typename B> R do_it(R (*func)(A, B), typename std::common_type<A>::type a, // not deduced typename std::common_type<B>::type b) // not deduced { return func(a, b); } 

Now A and B defined purely by the signature of the function pointer:

 int foo(int &, int const &); int main() { int a = 0, b = 0; return do_it(foo, a, b); // deduces A = int &, B = int const & } 

(Note that std::common_type<T>::type is the recommended idiom for an identification type whose sole purpose is to remove the template argument from the output of the argument. This was previously called things like identity<T>::type or alias<T> , but the standard library attribute std::common_type serves for this purpose just fine.)

+5
source

When deriving template arguments using the value argument, you will only get the value type. That is, when you use a function template, for example

 template <typename T> void f(T) { } 

type T will always be a non-reference type. Now, when you try to pass a pointer to a function and a value, the compiler cannot make consecutive inferred types consistent if the function does not accept a value type:

 template <typename T> void f(void (*)(T), T) {} void f0(int); void f1(int const&); int main() { f(&f0, 0); // OK f(&f1, 0); // ERROR } 

One way to solve this problem is to correctly overload the corresponding function template. If you add the function below to the mix, the above example works again:

 template <typename T> void f(void (*)(T const&), T const&) {} 

Clearly, this is quickly becoming a service nightmare and probably not what you want to do. An alternative is to use different template parameters for the corresponding arguments:

 template <typename T, typename S> void f(void (*)(T), S) {} 

Although this works, it has a direct effect on the fact that you do not have to match the type that you really want to match for the second argument: it will be a value type, even if you might want to get a reference type (personally, I doubt that you are doing this, but this is another problem). If you do not want this to happen, you can prevent the compiler form from outputting arguments for some argument. For instance:

 template <typename T> struct helper { typedef T type; }; template <typename T> void f(void (*)(T), typename helper<T>::type) {} 

Although the above example just demonstrates the problem at hand using only one template argument, I'm sure this works with a lot of template arguments. Is this what Boost does not know and does not care.

+2
source

Source: https://habr.com/ru/post/1435784/


All Articles