I have a problem with detecting when an instantiation of a generic lambda is well formed but not compilable, and detecting it has stumped me:
#include <functional>
class future
{
public:
int get() & { return 5; }
};
// Gets the return type of F(A), returning a not_well_formed type if not well formed
template<class F, class A> struct get_return_type
{
struct not_well_formed {};
template<class _F, class _A> static not_well_formed test(...);
template<class _F, class _A> static auto test(_F &&f) noexcept(noexcept(f(std::declval<_A>()))) -> decltype(f(std::declval<_A>()));
using type = decltype(test<F, A>(std::declval<F>()));
static constexpr bool is_noexcept = noexcept(test<F, A>(std::declval<F>()));
};
int main(void)
{
auto foo=[](auto &&x) { return x.get(); };
using type=get_return_type<decltype(foo), const future>::type;
return 0;
}
This fails with (on clang 3.7):
ned#kate:~$ clang++-3.7 -std=c++14 -o weird_generic_lambda_thing weird_generic_lambda_thing.cpp
weird_generic_lambda_thing.cpp:21:34: error: member function 'get' not viable: 'this' argument has type 'const future', but
function is not marked const
auto foo=[](auto &&x) { return x.get(); };
^
weird_generic_lambda_thing.cpp:14:111: note: in instantiation of function template specialization 'main()::(anonymous
class)::operator()<const future>' requested here
..._F, class _A> static auto test(_F &&f) noexcept(noexcept(f(std::declval<_A>()))) -> decltype(f(std::declval<_A>()));
^
weird_generic_lambda_thing.cpp:15:25: note: while substituting explicitly-specified template arguments into function
template 'test'
using type = decltype(test<F, A>(std::declval<F>()));
^
weird_generic_lambda_thing.cpp:22:14: note: in instantiation of template class 'get_return_type<(lambda at
weird_generic_lambda_thing.cpp:21:12), const future>' requested here
using type=get_return_type<decltype(foo), const future>::type;
^
weird_generic_lambda_thing.cpp:6:7: note: 'get' declared here
int get() & { return 5; }
^
1 error generated.
You can probably blame my inexperience with Expression SFINAE here (thanks Visual Studio!), but I am surprised: surely the decltype creating the return type of test() should fail to substitute if f(std::declval<_A>()) is not well formed?
Obviously the answer is that is does fail to substitute, but in a non-SFINAE way. Can the above be fixed so it correctly returns a not_well_formed type if the generic lambda is uncompilable with some arbitrary parameter type?
You cannot in general. Only early failures can be detected via SFINAE. Early failures are basically the declaration, not the definition, of a function (or class) template.
The lambda can provide SFINAE early failure instrumentation via explicitly declaring the return type ->decltype(x.get()), or through other SFINAE techniques like enable_if_t or void_t.
The idea is that compilers are not required to fully compile functions in order to engage in overload resolution.
Related
I have a container-like class with a method that works similarly to std::apply. I would like to overload this method with a const qualifier, however when I try to invoke this method with a generic lambda I get a hard error from instantiation of std::invoke_result_t. I am using std::invoke_result_t to deduce a return value of the method as well as to perform a SFINAE check of the argument.
#include <type_traits>
#include <utility>
template <typename T>
class Container
{
public:
template <typename F>
std::invoke_result_t<F, T &> apply(F &&f)
{
T dummyValue;
return std::forward<F>(f)(dummyValue);
}
template <typename F>
std::invoke_result_t<F, const T &> apply(F &&f) const
{
const T dummyValue;
return std::forward<F>(f)(dummyValue);
}
};
int main()
{
Container<int> c;
c.apply([](auto &&value) {
++value;
});
return 0;
}
The error message while compiling with Clang 6.0:
main.cc:27:9: error: cannot assign to variable 'value' with const-qualified type 'const int &&'
++value;
^ ~~~~~
type_traits:2428:7: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const int &>' requested here
std::declval<_Fn>()(std::declval<_Args>()...)
^
type_traits:2439:24: note: while substituting deduced template arguments into function template '_S_test' [with _Fn = (lambda at main.cc:26:13), _Args = (no value)]
typedef decltype(_S_test<_Functor, _ArgTypes...>(0)) type;
^
type_traits:2445:14: note: in instantiation of template class 'std::__result_of_impl<false, false, (lambda at main.cc:26:13), const int &>' requested here
: public __result_of_impl<
^
type_traits:2831:14: note: in instantiation of template class 'std::__invoke_result<(lambda at main.cc:26:13), const int &>' requested here
: public __invoke_result<_Functor, _ArgTypes...>
^
type_traits:2836:5: note: in instantiation of template class 'std::invoke_result<(lambda at main.cc:26:13), const int &>' requested here
using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
^
main.cc:16:10: note: in instantiation of template type alias 'invoke_result_t' requested here
std::invoke_result_t<F, const T &> apply(F &&f) const
^
main.cc:26:7: note: while substituting deduced template arguments into function template 'apply' [with F = (lambda at main.cc:26:13)]
c.apply([](auto &&value) {
^
main.cc:26:23: note: variable 'value' declared const here
c.apply([](auto &&value) {
~~~~~~~^~~~~
I'm not sure whether std::invoke_result_t is SFINAE-friendly, but I don't think that's the problem here since I've tried replacing it with a trailing return type, e.g.:
auto apply(F &&f) const -> decltype(std::declval<F>()(std::declval<const T &>()))
and got a similar error:
main.cc:27:9: error: cannot assign to variable 'value' with const-qualified type 'const int &&'
++value;
^ ~~~~~
main.cc:16:41: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const int &>' requested here
auto apply(F &&f) const -> decltype(std::declval<F>()(std::declval<const T &>()))
^
main.cc:26:7: note: while substituting deduced template arguments into function template 'apply' [with F = (lambda at main.cc:26:13)]
c.apply([](auto &&value) {
^
main.cc:26:23: note: variable 'value' declared const here
c.apply([](auto &&value) {
~~~~~~~^~~~~
Questions:
Why does this happen? More accurately, why is lambda's body instantiated during, what seems to be, an overload resolution?
How do I work around it?
Lambdas have deduced return type, unless you specify the return type explicitly. Thus, std::invoke_result_t has to instantiate the body in order to determine the return type. This instantiation is not in the immediate context, and causes a hard error.
You can make your code compile by writing:
[](auto &&value) -> void { /* ... */ }
Here, the body of the lambda won't be instantiated until the body of apply, and you're in the clear.
So overload resolution is a bit dumb here.
It doesn't say "well, if non-const apply works, I'll never call const apply, so I won't bother considering it".
Instead, overload resolution evalutes every possible candidate. It then eliminates those that suffer substitution failure. Only then does it order the candidates and pick one.
So both of these have F substituted into them:
template <typename F>
std::invoke_result_t<F, T &> apply(F &&f)
template <typename F>
std::invoke_result_t<F, const T &> apply(F &&f) const
I removed their bodies.
Now, what happens when you pass the lambda type of F to these?
Well, lambdas have the equivalent of an auto return type. In order to find out the actual return type when passed something, the compiler must examine the body of the lambda.
And SFINAE does not work when examining the bodies of functions (or lambdas). This is intended to make the compiler's job easier (as SFINAE is very hard for compilers, having them have to compile arbitrary code and run into arbitrary errors and then roll it back was a huge barrier).
We can avoid instanting the body of the lambda with this:
[](auto &&value) -> void { /* ... */ }
after you do that, both overloads of apply:
template <typename F>
std::invoke_result_t<F, T &> apply(F &&f)
template <typename F>
std::invoke_result_t<F, const T &> apply(F &&f) const
can have the return value evaluated (it is just void) and we get:
template <typename F=$lambda$>
void apply(F &&f)
template <typename F=$lambda$>
void apply(F &&f) const
now, note that apply const still exists. If you call apply const, you'll get the hard error caused by instantiating that lambda body.
If you want the lambda itself to be SFINAE friendly, you should need do this:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
[](auto &&value) RETURNS(++value)
note that this lambda is slightly different, as it returns a reference to the value. We can avoid this with:
[](auto &&value) RETURNS((void)++value)
and now the lambda is both SFINAE friendly and has the same behavior as your original lambda and your original program compiles as-is with this change.
The side effect of this is that non-const apply is now eliminated from overload resolution by SFINAE. Which makes it SFINAE friendly in turn.
There was a proposal to take RETURNS and rename it =>, but last I checked it was not accepted for c++20.
Consider the code:
class Test {
public:
template<int N> auto foo() {}
template<> auto foo<0>() { return 7; }
template<int N> void bar() {}
template<> int bar<0>() { return 7; }
};
I have tested the code with different compilers (through Compiler Explorer).
In case of Clang 7.0.0 foo compiles, while bar is giving an error:
:8:20: error: no function template matches function template
specialization 'bar'
template<> int bar<0>() { return 7; }
^
:7:26: note: candidate template ignored: could not match 'void
()' against 'int ()'
template<int N> void bar() {};
^
Visual C++ agrees (MSVC 19 2017 RTW):
(8): error C2912: explicit specialization 'int
Test::bar(void)' is not a specialization of a function template
gcc 8.2 does not compile any of the code (though the reason is probably a bug in C++17 support:
:5:14: error: explicit specialization in non-namespace scope
'class Test'
template<> auto foo<0>() { return 7; };
^
:5:28: error: template-id 'foo<0>' in declaration of primary
template
template<> auto foo<0>() { return 7; };
^
:7:26: error: too many template-parameter-lists
template<int N> void bar() {};
^~~
:8:14: error: explicit specialization in non-namespace scope
'class Test'
template<> int bar<0>() { return 7; }
^
:8:20: error: expected ';' at end of member declaration
template<> int bar<0>() { return 7; }
^~~
;
:8:23: error: expected unqualified-id before '<' token
template<> int bar<0>() { return 7; }
^
What is the correct interpretation here? Can I have a different return type for different method specializations (and why only with auto, but not while specifying them explicitly)? With my limited understanding of auto and templates I would go with saying "no". I don't understand why would using auto instead of explicitly naming the return type allow to have different return type for different specializations.
However, those codes are simplified versions of the code that I have found elsewhere, so maybe my interpretation is incorrect - and in that case I would be grateful for the explanation why different return type is allowed when auto is used for specialization, while explicitly naming the type seems to be forbidden.
There are several different problems with the example code.
1) GCC fails to implement CWG 727 (required in C++17): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 which causes error: explicit specialization in non-namespace scope 'class Test'
2) If we ignore that, the example code can be simplified to just
template<int N> auto foo() {}
template<> auto foo<0>() { return 7; }
template<int N> void bar() {}
template<> int bar<0>() { return 7; }
which still exhibits the same errors. Now all compilers agree on the output. They compile foos and error out on the bar specialization.
why different return type is allowed when auto is used for specialization
It is allowed by a standard to specialize functions with auto return type if the specializations also have auto placeholder
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type
http://eel.is/c++draft/dcl.spec.auto#11
So by virtue of this specialization conforming to the standard (being similar to one of the given examples) and not being explicitly forbidden anywhere it's allowed.
As for the error with bar, the standard says that return type is a part of function template signature:
⟨function template⟩ name, parameter type list ([dcl.fct]), enclosing namespace (if any), return type, template-head, and trailing requires-clause ([dcl.decl]) (if any)
http://eel.is/c++draft/defns.signature.templ
As such, in compiler's eyes template<> int bar<0>() { return 7; } is a specialization of template<... N> int bar(); template (notice the return type). But it wasn't declared previously (specialization can't precede declaration) so compilation fails! If you add template<int N> int bar(); then it'll compile (but complain about call ambiguity if you try to call bar).
Basically, you can't change the function signature in a specialization, you can only specialize (duh) template parameters (which should also be exactly the same as in declaration, since it's also part of the signature).
Can I have a template specialization with explicitly declared return type different from the base template at all
As explained - you can't change the function template signature which means you can't change the return type. BUT, you can specialize the return type if it depends on a template parameter!
Consider
template<int N, typename R = void> R bar() {}
template<> int bar<0>() { return 7; }
// bar<0, int> is deduced, see: http://eel.is/c++draft/temp.expl.spec#10
This is allowed but it has a disadvantage of having to write bar<0, int> when you want to make a call to a specialization: https://godbolt.org/z/4lrL62
This can be worked around by making the type conditional in the original declaration: https://godbolt.org/z/i2aQ5Z
But it'll quickly become cumbersome to maintain once the number of specializations grows.
Another, perhaps a bit more maintainable option, would be to return something like ret_type<N>::type and specialize that along with bar. But it's still won't be as clean as with just using auto.
For a generic library I'm trying to define a concept in terms of having a correct implementation of a traits struct. In particular I want to check that the user has provided all required nested types, static member functions and data members. However, I can't find a way to require a nested templated type(-alias).
I have a declaration of the traits struct
template <typename>
struct trait;
and a specialization for chars
template <>
struct trait<char> {
template <typename>
using type = int;
};
I now define my concept in terms of this trait
template <typename T>
concept bool SatisfiesTrait = requires() {
typename trait<T>; // require the user to have spcialized
// for their type
typename trait<T>::type<long long>; // require the nested template
};
as well as a function requiring a type satisfying this concept
constexpr bool foo(SatisfiesTrait) { return true; }
In my main method I then try to call this function with a char:
int main() {
foo('c');
}
When compiling all this with GCC I get the error message
prog.cc:15:24: error: 'trait<T>::type' is not a type
typename trait<T>::type<long long>;
^~~~
prog.cc: In function 'int main()':
prog.cc:26:11: error: cannot call function 'constexpr bool foo(auto:1) [with auto:1 = char]'
foo('c');
^
prog.cc:18:16: note: constraints not satisfied
constexpr bool foo(SatisfiesTrait) {
^~~
prog.cc:18:16: note: in the expansion of concept 'SatisfiesTrait<auto:1>' template<class T> concept const bool SatisfiesTrait<T> [with T = char]
However, when I change my main function to
int main() {
typename trait<char>::type<long long> v;
(void) v;
foo('c');
}
and comment out the requirement of the nested alias template it compiles just fine. The same problem occurs when the nested type has a non-type template parameter instead of a type parameter.
Am I doing something wrong here or is this a bug in GCCs implementation of the Concepts TS?
The code can also be found on Wandbox.
Sorry for how complicated the title of this question is; I tried to describe the minimal SSCCE I constructed for this problem.
I have the following code:
#include <iostream>
namespace fizz
{
template<typename... Ts>
class bar
{
public:
template<int I, typename... Us>
friend auto foo(const bar<Us...> &);
private:
int i = 123;
};
template<int I, typename... Ts>
auto foo(const bar<Ts...> & b)
{
return b.i;
}
}
int main()
{
std::cout << fizz::foo<1>(fizz::bar<int, float>{});
}
This code compiles with GCC 5.2 and doesn't with Clang 3.7:
main.cpp:19:18: error: 'i' is a private member of 'fizz::bar<int, float>'
return b.i;
^
main.cpp:25:24: note: in instantiation of function template specialization 'fizz::foo<1, int, float>' requested here
std::cout << fizz::foo<1>(fizz::bar<int, float>{});
^
main.cpp:13:13: note: declared private here
int i = 123;
^
However, if you change the code slightly (although in a way that is not exactly useful for me, since in the real code this would introduce tons of boilerplate):
#include <iostream>
namespace fizz
{
template<typename... Ts>
class bar
{
public:
template<int I, typename... Us>
friend int foo(const bar<Us...> &);
private:
int i = 123;
};
template<int I, typename... Ts>
int foo(const bar<Ts...> & b)
{
return b.i;
}
}
int main()
{
std::cout << fizz::foo<1>(fizz::bar<int, float>{});
}
it suddenly works with that Clang 3.7.
The difference is that in the version of the code that doesn't compile with Clang, the friend function template uses C++14 auto return type deduction, while the working one plainly says it returns int. The same problem also happens with other variants of auto return type deduction, like auto && or const auto &.
Which compiler is right? Please provide some standard quotes to support the answer, since it is quite possible that a bug will need to be filed for one (...hopefully not both) compilers... or a standard defect, if both are right (which wouldn't be the first time).
I believe it's a clang bug. I want to approach it from this direction. What wrinkles does the auto placeholder type add, as compared to having a specified return type? From [dcl.spec.auto]:
The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid. If the function declarator includes a trailing-return-type (8.3.5), that trailing-return-type specifies the declared return type
of the function. Otherwise, the function declarator shall declare a function. If the declared return type of
the function contains a placeholder type, the return type of the function is deduced from return statements
in the body of the function, if any.
auto can appear in foo's declaration and definition, and is valid.
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression,
the program is ill-formed. Once a return statement has been seen in a function, however, the return type
deduced from that statement can be used in the rest of the function, including in other return statements. [ Example:
auto n = n; // error, n’s type is unknown
auto f();
void g() { &f; } // error, f’s return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum’s return type is int
else
return sum(i-1)+i; // OK, sum’s return type has been deduced
}
—end example ]
The first time we need to use determine the type of an expression, the return type of the function will already have been deduced from the return in the definition of foo(), so this is still valid.
Redeclarations or specializations of a function or function template with a declared return type that uses a
placeholder type shall also use that placeholder, not a deduced type.
We're using auto in both places, so we don't violate this rule either.
In short, there are several things that differentiate a specific return type from an placeholder return type from a function declaration. But all the usages of auto in the example are correct, so the namespace-scope foo should be seen as a redeclaration and definition of the first-declared friend auto foo within class template bar. The fact that clang accepts the former as a redeclaration for return type int but not for auto, and there is no relevant different for auto, definitely suggests this is a bug.
Further, if you drop the int I template parameter so that you can call foo unqualified, clang will report the call as ambiguous:
std::cout << foo(fizz::bar<int, float>{});
main.cpp:26:18: error: call to 'foo' is ambiguous
std::cout << foo(fizz::bar<int, float>{});
^~~
main.cpp:10:21: note: candidate function [with Us = <int, float>]
friend auto foo(const bar<Us...> &);
^
main.cpp:17:10: note: candidate function [with Ts = <int, float>]
auto foo(const bar<Ts...>& b)
^
So we have two function templates named foo in the same namespace (since from [namespace.memdef] the friend declaration for foo will place it in the nearest enclosing namespace) that take the same arguments and have the same return type (auto)? That shouldn't be possible.
It appears that your first example should work. There is a statement in C++14 (7.1.6.4 p12):
Redeclarations or specializations of a function or function template with a declared return type that uses a
placeholder type shall also use that placeholder, not a deduced type. [ Example:
.
.
.
template <typename T> struct A {
friend T frf(T);
};
auto frf(int i) { return i; } // not a friend of A<int>
The reason for the example appears to be to explain that to make the declarations match (and cause the defined function to be a friend) the declaration of frf inside struct A would also need to use auto. This implies to me that having a friend declaration with an auto return type and later defining the friend function (and also using auto) is allowed. I can't find anything that would make this work differently for a member function template, like in your example.
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;
}