The following code:
struct A
{
using this_t = A;
template <class F1>
void ins(const F1& fu)
{
}
template <class F1, class... Args>
void ins(const F1& fu, const Args&... args)
{
this_t::ins<Args...>(args...);
// the line which is causing the error:
constexpr decltype(&this_t::ins<Args...>) func = &this_t::ins<Args...>;
}
};
int main()
{
A a;
a.ins(5, 5, 5, 5, 5);
}
Gives this error:
error: reference to overloaded function could not be resolved
As shown here: https://godbolt.org/z/nJiY4A
What's going wrong here and how is it possible to get the pointer to the function then?
I think I know why that line fails to compile. It has little to do with overload resolution, and everything to do with decltype:
[over.over]/1 A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. A function with type F is selected for the function type FT of the target type required in the context if F (after possibly applying the function pointer conversion (7.13)) is identical to FT. The target can be
(1.1) — an object or reference being initialized (11.6, 11.6.3, 11.6.4),
(1.2) — the left side of an assignment (8.18),
(1.3) — a parameter of a function (8.2.2),
(1.4) — a parameter of a user-defined operator (16.5),
(1.5) — the return value of a function, operator function, or conversion (9.6.3),
(1.6) — an explicit type conversion (8.2.3, 8.2.9, 8.4), or
(1.7) — a non-type template-parameter (17.3.2).
The overloaded function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed.
Emphasis mine. Just mentioning an overloaded function name outside of those enumerated contexts - e.g. in decltype(&this_t::ins<F2, Args...>) - renders the program ill-formed. Avoiding this problematic usage by spelling the type explicitly makes the code compile:
void (this_t::*func)(const Args&...) = &this_t::ins<Args...>;
I'm not yet sure why taking an address of the overloaded function behaves differently than calling it; why the former produces an ambiguity while the latter is able to resolve the overload successfully.
Meanwhile, here's one possible workaround:
struct A
{
using this_t = A;
template <class F1>
void ins(const F1& fu)
{
}
template <class F1, class F2, class... Args>
void ins(const F1& f1, const F2& f2, const Args&... args)
{
this_t::ins<F2, Args...>(f2, args...);
// the line which is causing the error:
constexpr decltype(&this_t::ins<F2, Args...>) func = &this_t::ins<F2, Args...>;
}
};
The tweak is to make the first overload callable with one argument, and the second with two or more. Demo
Not exactly what you asked but... if you can use an empty (do nothing, nothing receiving) ground recursion case, I propose the following solution
struct A
{
using this_t = A;
template <int = 0>
void ins ()
{ }
template <typename F1, typename ... Args>
void ins (F1 const &, Args const & ... args)
{
this_t::ins<Args...>(args...);
// no more error
constexpr decltype(&this_t::ins<Args...>) func = &this_t::ins<Args...>;
}
};
This way, calling ins<Args...>, you can avoid all ambiguities when Args... is only one type (only the recursion case matches) and when Args... is empty, matches the ground case thanks to the int = 0 template default value.
Related
I have the two functions that are almost the same (with the exception that one of them is a template):
int* bar(const std::variant<int*, std::tuple<float, double>>& t)
{
return std::get<0>(t);
}
template <typename... Args>
int* foo(const std::variant<int*, std::tuple<Args...>>& t)
{
return std::get<0>(t);
}
Than, they are use like this:
foo(nullptr);
bar(nullptr);
The second one compiles and returns (int*)nullptr, but the first one doesn't (in Visual Studio 2019 using C++17 giving the error foo: no matching overload found). Why? Why does making this function a template cause it to cease to compile?
Using foo like below doesn't help either, so the inability to deduce Args is probably not the problem:
foo<>(nullptr);
In contrary, the following does work:
foo(std::variant<int*, std::tuple<>>(nullptr));
Is it possible to somehow avoid the need to write this in such a long manner?
Apparently if a type of a function parameter depends on a template parameter that has to be deduced (because it's not specified in <...>), then implicit conversions don't apply when passing an argument to that parameter.
Source:
The function parameters that do not participate in template argument
deduction (e.g. if the corresponding template arguments are explicitly
specified) are subject to implicit conversions to the type of the
corresponding function parameter (as in the usual overload
resolution).
A template parameter pack that is explicitly specified may be extended
by template argument deduction if there are additional arguments:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
This also explains why foo<>(nullptr); still doesn't work. Since the compiler tries to deduce additional types to extend Args, in this case there doesn't seem to be any difference between foo(nullptr); and foo<>(nullptr);.
When a template function is considered it will only work for an exact match of the argument types at the call. This means that no conversions will be made (except for cv qualifiers).
A simple workaround in your case would be to make a function catch std::nullptr_t and forward that to your template.
int* foo(std::nullptr_t) {
return foo(std::variant<int*, std::tuple<>>{nullptr});
}
I would avoid this construct simply because the rules about exactly how the compiler will (if it even does it at all) resolve the overload are so confusing that I couldn't really tell you what it did without looking at a standards document, and code like that should be avoided. I would force the overload resolution you want this way instead:
template <typename... Args>
int *foo(const ::std::variant<int*, ::std::tuple<Args...>> &t)
{
return ::std::get<0>(t);
}
int *foo(int *ip)
{
using my_variant = ::std::variant<int *, ::std::tuple<>>;
return foo(my_variant{ip});
}
template <typename... Args>
int *foo(::std::tuple<Args...> const &t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{t});
}
template <typename... Args>
int *foo(::std::tuple<Args...> &&t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{::std::move(t)});
}
Consider this example of code:
#include <iostream>
#include <functional>
typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;
struct X
{
X (func1_t f)
{ }
X (func2_t f)
{ }
};
int main ( )
{
X x([](){ std::cout << "Hello, world!\n"; });
}
I was sure that it shouldn't compile, because the compiler shouldn't be able to choose one of the two constructors. g++-4.7.3 shows this expected behavior: it says that call of overloaded constructor is ambiguous. However, g++-4.8.2 successfully compiles it.
Is this code correct in C++11 or it is a bug/feature of this version of g++?
In C++11...
Let's take a look at the specification of the constructor template of std::function (which takes any Callable): [func.wrap.func.con]/7-10
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible. f shall be Callable (20.10.11.2) for argument types ArgTypes and return type
R. The copy constructor and destructor of A shall not throw
exceptions.
8 Postconditions: !*this if any of the following hold:
f is a NULL function pointer.
f is a NULL pointer to member.
F is an instance of the function class template, and !f
9 Otherwise, *this targets a copy of f initialized with std::move(f). [left out a note here]
10 Throws: shall not throw exceptions when f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw
bad_alloc or any exception thrown by F’s copy or move constructor.
Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)> from a [](){} (i.e. with signature void(void)) violates the requirements of std::function<void(int)>'s constructor.
[res.on.required]/1
Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.
So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.
In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible.
8 Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.
[...]
The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo,
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
and a call-expression such as
foo([](std::string){}) // (C)
then the second overload of foo is chosen unambiguously: Since std::function<F> defines F as its interface to the outside, the F defines which argument types are passed into std::function. Then, the wrapped function object has to be called with those arguments (argument types). If a double is passed into std::function, it cannot be passed on to a function taking a std::string, because there's no conversion double -> std::string.
For the first overload of foo, the argument [](std::string){} is therefore not considered Callable for std::function<void(double)>. The constructor template is deactivated, hence there's no viable conversion from [](std::string){} to std::function<void(double)>. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.
Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R of a std::function<R(ArgTypes...)> is void, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {} and []() -> bool {} are Callable for std::function<void()>. The following situation therefore produces an ambiguity:
void foo(std::function<void()>);
void foo(std::function<bool()>);
foo([]() -> bool {}); // ambiguous
The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo are viable (first of all) and neither is better.
How can SFINAE help here?
Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:
#include <type_traits>
#include <iostream>
template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }
template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }
int main()
{
foo(42);
foo(42.);
}
Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:
#include <type_traits>
#include <iostream>
struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};
struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};
struct kitty
{
kitty(foo) {}
kitty(bar) {}
};
int main()
{
kitty cat(42);
kitty tac(42.);
}
It is totally valid. Since c++11 lambda expressions (and your std::function wrapper) create function objects. The great strength of function objects is that, even when they are generic, they remain first-class objects. Unlike ordinary function templates, they can be passed to and returned from functions.
You can create operator overload sets explicitly with inheritance and using declarations. The following usage from Mathias Gaunard demonstrates “overloaded lambda expressions".
template <class F1, class F2>
struct overload_set : F1, F2
{
overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
using F1::operator();
using F2::operator();
};
template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
return overload_set<F1,F2>(x1,x2);
}
auto f = overload(
[](){return 1;},
[](int x){return x+1;}
);
int x = f();
int y = f(2);
source
EDIT: Maybe it'll become more clear if in the provided example you replace
F1 -> std::function<void()>
F2 -> std::function<void(int)>
and see it compile in gcc4.7
The templated solution was only provide to demonstrate that concept scales to generic code and dissambiguation is possible.
In your case, when using an older compiler like gcc 4.7, you could help by explicit cast and gcc will work things out, as you can see in this live example
Just in case you're wondering, it wouldn't work if you cast the other way around (try to convert the lambda taking int to the std::function taking no arguments and so on)
I'm experimenting with resolving the address of an overloaded function (bar) in the context of another function's parameter (foo1/foo2).
struct Baz {};
int bar() { return 0; }
float bar(int) { return 0.0f; }
void bar(Baz *) {}
void foo1(void (&)(Baz *)) {}
template <class T, class D>
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
int main() {
foo1(bar); // Works
foo2<Baz>(bar); // Fails
}
There's no trouble with foo1, which specifies bar's type explicitly.
However, foo2, which disable itself via SFINAE for all but one version of bar, fails to compile with the following message :
main.cpp:19:5: fatal error: no matching function for call to 'foo2'
foo2<Baz>(bar); // Fails
^~~~~~~~~
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D'
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
^
1 error generated.
It is my understanding that C++ cannot resolve the overloaded function's address and perform template argument deduction at the same time.
Is that the cause ? Is there a way to make foo2<Baz>(bar); (or something similar) compile ?
As mentioned in the comments, [14.8.2.1/6] (working draft, deducing template arguments from a function call) rules in this case (emphasis mine):
When P is a function type, function pointer type, or pointer to member function type:
If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set the parameter is treated as a non-deduced context.
SFINAE takes its part to the game once the deduction is over, so it doesn't help to work around the standard's rules.
For further details, you can see the examples at the end of the bullet linked above.
About your last question:
Is there a way to make foo2<Baz>(bar); (or something similar) compile ?
Two possible alternatives:
If you don't want to modify the definition of foo2, you can invoke it as:
foo2<Baz>(static_cast<void(*)(Baz *)>(bar));
This way you explicitly pick a function out of the overload set.
If modifying foo2 is allowed, you can rewrite it as:
template <class T, class R>
auto foo2(R(*d)(T*)) {}
It's more or less what you had before, no decltype in this case and a return type you can freely ignore.
Actually you don't need to use any SFINAE'd function to do that, deduction is enough.
In this case foo2<Baz>(bar); is correctly resolved.
Some kind of the general answer is here: Expression SFINAE to overload on type of passed function pointer
For the practical case, there's no need to use type traits or decltype() - the good old overload resolution will select the most appropriate function for you and break it into 'arguments' and 'return type'. Just enumerate all possible calling conventions
// Common functions
template <class T, typename R> void foo2(R(*)(T*)) {}
// Different calling conventions
#ifdef _W64
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {}
#else
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {}
#endif
// Lambdas
template <class T, class D>
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {}
It could be useful to wrap them in a templated structure
template<typename... T>
struct Foo2 {
// Common functions
template <typename R> static void foo2(R(*)(T*...)) {}
...
};
Zoo2<Baz>::foo2(bar);
Although, it will require more code for member functions as they have modifiers (const, volatile, &&)
Consider this example of code:
#include <iostream>
#include <functional>
typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;
struct X
{
X (func1_t f)
{ }
X (func2_t f)
{ }
};
int main ( )
{
X x([](){ std::cout << "Hello, world!\n"; });
}
I was sure that it shouldn't compile, because the compiler shouldn't be able to choose one of the two constructors. g++-4.7.3 shows this expected behavior: it says that call of overloaded constructor is ambiguous. However, g++-4.8.2 successfully compiles it.
Is this code correct in C++11 or it is a bug/feature of this version of g++?
In C++11...
Let's take a look at the specification of the constructor template of std::function (which takes any Callable): [func.wrap.func.con]/7-10
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible. f shall be Callable (20.10.11.2) for argument types ArgTypes and return type
R. The copy constructor and destructor of A shall not throw
exceptions.
8 Postconditions: !*this if any of the following hold:
f is a NULL function pointer.
f is a NULL pointer to member.
F is an instance of the function class template, and !f
9 Otherwise, *this targets a copy of f initialized with std::move(f). [left out a note here]
10 Throws: shall not throw exceptions when f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw
bad_alloc or any exception thrown by F’s copy or move constructor.
Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)> from a [](){} (i.e. with signature void(void)) violates the requirements of std::function<void(int)>'s constructor.
[res.on.required]/1
Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.
So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.
In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible.
8 Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.
[...]
The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo,
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
and a call-expression such as
foo([](std::string){}) // (C)
then the second overload of foo is chosen unambiguously: Since std::function<F> defines F as its interface to the outside, the F defines which argument types are passed into std::function. Then, the wrapped function object has to be called with those arguments (argument types). If a double is passed into std::function, it cannot be passed on to a function taking a std::string, because there's no conversion double -> std::string.
For the first overload of foo, the argument [](std::string){} is therefore not considered Callable for std::function<void(double)>. The constructor template is deactivated, hence there's no viable conversion from [](std::string){} to std::function<void(double)>. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.
Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R of a std::function<R(ArgTypes...)> is void, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {} and []() -> bool {} are Callable for std::function<void()>. The following situation therefore produces an ambiguity:
void foo(std::function<void()>);
void foo(std::function<bool()>);
foo([]() -> bool {}); // ambiguous
The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo are viable (first of all) and neither is better.
How can SFINAE help here?
Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:
#include <type_traits>
#include <iostream>
template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }
template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }
int main()
{
foo(42);
foo(42.);
}
Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:
#include <type_traits>
#include <iostream>
struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};
struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};
struct kitty
{
kitty(foo) {}
kitty(bar) {}
};
int main()
{
kitty cat(42);
kitty tac(42.);
}
It is totally valid. Since c++11 lambda expressions (and your std::function wrapper) create function objects. The great strength of function objects is that, even when they are generic, they remain first-class objects. Unlike ordinary function templates, they can be passed to and returned from functions.
You can create operator overload sets explicitly with inheritance and using declarations. The following usage from Mathias Gaunard demonstrates “overloaded lambda expressions".
template <class F1, class F2>
struct overload_set : F1, F2
{
overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
using F1::operator();
using F2::operator();
};
template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
return overload_set<F1,F2>(x1,x2);
}
auto f = overload(
[](){return 1;},
[](int x){return x+1;}
);
int x = f();
int y = f(2);
source
EDIT: Maybe it'll become more clear if in the provided example you replace
F1 -> std::function<void()>
F2 -> std::function<void(int)>
and see it compile in gcc4.7
The templated solution was only provide to demonstrate that concept scales to generic code and dissambiguation is possible.
In your case, when using an older compiler like gcc 4.7, you could help by explicit cast and gcc will work things out, as you can see in this live example
Just in case you're wondering, it wouldn't work if you cast the other way around (try to convert the lambda taking int to the std::function taking no arguments and so on)
I have the following template class which acts as a proxy. It has a method named call which is supposed to be used to call methods on the wrapped object. There's a problem with it. The type deduction fails and I cannot understand why.
Hudsucker::f takes an std::string and then no matter if I pass an std::string or a const reference to it the compiler is able to call the right method.
But in case of Hudsucker::g with takes a const reference to std::string type deduction fails in both cases with both GCC and Clang.
GCC error for the first line:
main.cpp:36:28: error: no matching function for call to ‘Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&)’
main.cpp:36:28: note: candidate is:
main.cpp:10:10: note: template<class A> void Proxy::call(void (T::*)(A), A) [with A = A; T = Hudsucker]
main.cpp:10:10: note: template argument deduction/substitution failed:
main.cpp:36:28: note: deduced conflicting types for parameter ‘A’ (‘const std::basic_string<char>&’ and ‘std::basic_string<char>’)
Especially this bit is strange: no matching function for call to Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&). That is exactly the signature I would expect to see work.
Clang error for the first line:
main.cpp:36:7: error: no matching member function for call to 'call'
p.call(&Hudsucker::g, s); // <- Compile error
~~^~~~
main.cpp:10:10: note: candidate template ignored: deduced conflicting types for parameter 'A' ('const std::basic_string<char> &' vs. 'std::basic_string<char>')
void call(void (T::*f)(A), A a)
Code:
#include <string>
#include <iostream>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A>
void call(void (T::*f)(A), A a)
{
(o_.*f)(a);
}
private:
T &o_;
};
class Hudsucker
{
public:
void f(std::string s) {}
void g(std::string const &s) {}
};
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
p.call(&Hudsucker::g, s); // <- Compile error
p.call(&Hudsucker::g, r); // <- Compile error
return 0;
}
Could you explain why the type deduction fails in that way? Is there a way to get this to compile with const references?
The compiler cannot deduce the type A, since it has contrasting information. From the type of the member function, it would deduce A to be std::string const&, while from the type of the second argument, it would deduce it to be std::string.
Change your function template into one that allows different types for the parameter of the member function and the argument actually provided, and then SFINAE-constrain the latter to be convertible to the former:
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
If you are wondering why from this function call:
std::string const s = "For kids, you know.";
// ...
p.call(&Hudsucker::g, s);
The compiler would deduce std::string, that's because of paragraph 14.8.2.1/2 of the C++11 Standard:
If P is not a reference type:
— If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is
used in place of A for type deduction; otherwise,
— If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3)
is used in place of A for type deduction; otherwise,
— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
In the quoted paragraph, P is your A (from your function template) and A is std::string const. This means the const in std::string const is ignored for type deduction. To see this better, consider this simpler example:
#include <type_traits>
template<typename T>
void foo(T t)
{
// Does NOT fire!
static_assert(std::is_same<T, int>::value, "!");
}
int main()
{
int const x = 42;
foo(x);
}
Considering the second function call:
std::string const &r = s;
// ...
p.call(&Hudsucker::g, r);
The reason is that the type of the id-expression r is std::string const. The reference is dropped because of paragraph 5/5:
If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to
any further analysis. The expression designates the object or function denoted by the reference, and the
expression is an lvalue or an xvalue, depending on the expression.
And now we're back to the same situation as for the first function call.
As pointed out by Mike Vine in the comments, you may want to perfectly-forward your second argument when giving it in input to the first (member function) argument during the function call:
#include <utility> // For std::forward<>()
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B&& a)
{
(o_.*f)(std::forward<B>(a));
}
If you cannot afford C++11, then you won't be allowed to use default arguments for template parameters. In that case, you can use the SFINAE-constraint on the return type:
template <typename A, typename B>
typename enable_if<is_convertible<B, A>::value>::type
// ^^^^^^^^^ ^^^^^^^^^^^^^^
// But how about these traits?
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
Notice, that std::enable_if and std::is_convertible are not part of the C++03 Standard Library. Fortunately, Boost has its own version of enable_if and is_convertible, so:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_convertible.hpp>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A, typename B>
typename boost::enable_if<boost::is_convertible<B, A>>::type
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
private:
T &o_;
};
Notice, that boost::enable_if accepts as its first template argument a type which defines a value boolean member, whereas std::enable_if accepts a boolean value. The equivalent of std::enable_if in Boost is boost::enable_if_c.
Seems to me a simpler solution would be to just exclude one of the two arguments from trying to deduce A, and the second one is the better candidate:
template <typename A>
void call(void (T::*f)(A), typename std::identity<A>::type a)
{
(o_.*f)(a);
}
If you don't have std::identity in your type traits, use this one:
template <typename T>
struct identity { typedef T type; };
Here's why this works: the compiler cannot deduce A from the second argument, since it's just a template parameter to something that a nested type is taken of. Basically, it can't pattern-match any incoming type against something_that_contains_A::type - due to template specialization, it can't reverse-engineer the argument from the definition of the left side. The net result is that the second argument is an "undeduced context". The compiler will not attempt to deduce A from there.
This leaves the first argument as the only place where A can be deduced from. With only one deduction result for A, it is not ambiguous and deduction succeeds. The compiler then proceeds to substitute the deduction result into every place where A was used, including the second argument.
You just need to pass template argument to template function when calling it in your main.
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
//just add template argument to template function call !!!
p.call< const std::string & > (&Hudsucker::g, s); // <- NO Compile error !!!!
p.call< const std::string & > (&Hudsucker::g, r); // <- NO Compile error !!!**
return 0;
}