Overload resolution and universal reference parameters - c++

The following code works and the overloads are found as expected:
struct HasBuzz
{
void buzz() const {}
};
struct NoBuzz {};
template <typename T>
void foo(T const& t)
{
t.buzz();
}
void foo(NoBuzz const&){}
int main()
{
foo(HasBuzz{});
foo(NoBuzz{});
}
However, if I replace the first overload with a "universal reference" version then it no longer works. The correct overload is not found for NoBuzz.
struct HasBuzz
{
void buzz() const {}
};
struct NoBuzz {};
template <typename T>
void foo(T&& t)
{
t.buzz();
}
void foo(NoBuzz const&){}
int main()
{
foo(HasBuzz{});
foo(NoBuzz{}); // error: NoBuzz has no member function buzz
}
What can I do to make it work?

Simple solution
Add an overload that is callable with an rvalue of type NoBuzz.
void foo(NoBuzz const&){ };
void foo(NoBuzz&&) { }; // overload for rvalues
Note: Depending on your actual use case this might not be enough, because if you pass a non-const lvalue type NoBuzz to foo you'd still instantiate the template, since the two NoBuzz overloads doesn't match. At the end of this post is a more complex, but certainly cleaner, solution.
Explanation
template<class T>
void foo (T&&); // (A)
void foo (NoBuzz const&); // (B)
The problem with your snippet is that your template (A) can be instantiated in such a way that it's a better match than your overload (B).
When the compiler sees that you are trying to call a function named foo with an argument which is an rvalue of type NoBuzz, it will look for all functions named foo taking one argument where a NoBuzz would fit.
Let's say it starts of with your template (A), here it sees that T&& is deducable to any reference type (both lvalue, and rvalue), since we are passing an rvalue T = NoBuzz.
With T = NoBuzz the instantiated template would be semantically equivalent to:
void foo (NoBuzz&&); // (C), instantiated overload of template (A)
It will then continue to your overload (B). This overload accepts a const lvalue reference, which can bind to both lvalues and rvalues; but our previous template instantiation (C) can only bind to rvalues.
Since (C) is a better match than (B), binding rvalues to T&& is prefered over U const&, that overload is selected and you get the behavior you describe in your post.
Advanced solution
We can use a technique called SFINAE to conditionally make it impossible to call the template if the type passed doesn't implement .buzz ().
template <typename T>
auto foo(T&& t) -> decltype (t.buzz ())
{
return t.buzz();
}
The above solution uses a lot of new features of C++11, detailed information is available here:
wikipedia.org - C++11 - auto
wikipedia.org - decltype
Conditional overloading with trailing-return-type possible?

refp's answer explains the behavior you're seeing, and offers a possible solution. Another option is to ensure the foo function template does not make it into the candidate set for overload resolution unless T has a member function named buzz().
template <typename T>
auto foo(T&& t)
-> decltype((void)(t.buzz()), void())
{
t.buzz();
}
After making this change the foo(NoBuzz const&) overload will be selected when you pass it an instance of NoBuzz. Live demo
A detailed explanation of what's going on in the decltype expression in the trailing return type can be found here. The only thing I've done differently here is instead of using three subexpressions, with the middle one being void() to prevent a user defined operator, from being selected, I've cast the result of the first expression to void; the intent and result are identical in both cases.

Related

How does template type deduction work with an overloaded function as the parameter

Look at this (simplified) example:
int foo(int) { return 0;}
double foo(double) { return 0.0; }
template <class T>
enable_if<is_integral<T>::value>
bar(T(*f)(T)) {}
int main()
{
bar(foo);
return 0;
}
My expectation was that the compiler will first try to instantiate the template for each overload, which would fail (SFINAE) for the second overload and therefore there would be only void bar(int(*f)(int) left in the set of candidates, which would resolve using the first overload of foo. That is not what happens. It fails with something like this:
no matching function for call to ‘bar(<unresolved overloaded function type>)’
couldn't deduce template parameter ‘T’
Is there any way to achieve something like this?
C++'s type deduction is very simplistic. The clauses [temp.deduct.call]/6.2 and [over.over]/1 describe ways in which an overloaded name can be used as an argument.
In your example, both the deductions would succeed (to T=int and T=double) and one substitution would fail. But the language requires only one deduction succeed.
You asked how to achieve it. Here are some options:
Do not use an overloaded name, static_cast to the desired function type, or explicitly provide the type of T in the call to bar.
Alter the overloads such that deduction will succeed for only one of the overloads. In other words, exclude overloads by deduction, not by substitution. See below for an example.
Add another parameter which can deduce T.
Pass the buck and avoid deducing T until a later point.
Example for #2:
tempate<class T> struct integral_wrapper { T t; }
integral_wrapper<int> foo(int);
double foo(double);
template<class T>
void bar(integral_wrapper<T> foo(T));
Example for #3:
template<class T>
void bar(T (*f)(T), T);
bar(foo, 0);
Example for #4:
struct foo_t
{
int operator()(int);
double operator()(double);
} foo;
template<class F>
void bar(F);
bar(foo);
Note that a generic lambda can be another approach for #4.
Depending on your usage, some of these may be more attractive than others. Approach #4 is particularly useful when passing arguments to STL algorithms.

SFINAE and the address of an overloaded function

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, &&)

Template function as template argument

I'm learning about templates and tried to implement this method:
template <typename Func, typename Left, typename Right>
void flipArgs(Func* function, Left&& leftArg, Right&& rightArg) {
function(std::forward<Right>(rightArg), std::forward<Left>(leftArg));
}
It takes a function and two parameters and calls the given function with the two parameters flipped.
It works fine with function such as:
void test1(std::string, int) {
}
When I tried this function:
template <typename T>
void test2(T&& a, int) {
}
With:
string s("test");
flip(test2<string>, 42, s);
The compiler (g++ 4.7.1) tells me:
error: cannot bind 'std::basic_string' lvalue to 'std::basic_string&&'
I thought that a function parameter such as T&& was a special case that can bind to rvalue and lvalue references? What am I doing wrong?
I thought that a function parameter such as T&& was a special case that can bind to [rvalues and lvalues]?
It is. It basically means the template can have different instantiations for lvalues and for rvalues.
However... When you explicitly make T be string in test2<string>, you are picking one particular instantiation: void test2(string&&, int). string&& is no longer that special case. string&& can only bind to string rvalues. There isn't one instantiation that binds to both rvalues and lvalues.
In general, I'd recommend against explicitly passing function template parameters (unless those are intended, like std::forward or std::make_unique).
In this case, you could instead force one of the instantiations that binds to lvalues. Something like flip(test2<string&>, 42, s);, which will instantiate void test2(string&, int).
If you really want to pass an argument to flip that can accept both lvalues and rvalues, you need a polymorphic function object:
struct test2 {
template <typename T>
void operator()(T&& a, int) const {
}
};
flip(test2{}, 42, s);
The key here is that the decision of which specialisation to use is not made when passing the argument, but only later on when that argument is used.
For completeness, in C++14 you can actually create anonymous polymorphic function objects with the new lambda syntax:
auto test2 = [](auto&& a, int) {};
flip(test2, 42, s);

Use of std::forward in c++

I have come across a code, where std::forward is used. I have googled about it for a longtime and not able to understand its real purpose and use.
I have seen similar threads in stackoverflow, but still not clear. Can somebody explain it with a simple example?
PS: I have gone through this page, but still not able to appreciate its use. Please do not flag this question duplicate and rather try to help me out.
As the page you linked poses it:
This is a helper function to allow perfect forwarding of arguments
taken as rvalue references to deduced types, preserving any potential
move semantics involved.
When you have a named value, as in
void f1(int& namedValue){
...
}
or in
void f2(int&& namedValue){
...
}
it evaluates, no matter what, to an lvalue.
One more step. Suppose you have a template function
template <typename T>
void f(T&& namedValue){
...
}
such function can either be called with an lvalue or with an rvalue; however, no matter what, namedValue evaluates to an lvalue.
Now suppose you have two overloads of an helper function
void helper(int& i){
...
}
void helper(int&& i){
...
}
calling helper from inside f
template <typename T>
void f(T&& namedValue){
helper(namedValue);
}
will invariably call the first overload for helper, since namedValue is, well, a named value which, naturally, evaluates to an lvalue.
In order to get the second version called when appropriate (i.e. when f has been invoked with a rvalue parameter), you write
template <typename T>
void f(T&& namedValue){
helper( std::forward<T>(namedValue) );
}
All of this is expressed much concisely in the documentation by the following
The need for this function stems from the fact that all named values
(such as function parameters) always evaluate as lvalues (even those
declared as rvalue references), and this poses difficulties in
preserving potential move semantics on template functions that forward
arguments to other functions.
Each expression is in exactly one of the following two value categories: lvalue or rvalue.
Normally if you call a function like:
template<typename T>
void f(T t);
template<typename T>
void g(T t)
{
f(t);
}
The value category of the argument to g is lost between the call to g and f, because named parameters, like local variables, are always lvalues.
By using std::forward and adjusting the parameter to a "universal reference" that uses reference collapsing you can preserve the value category:
template<typename T>
void f(T&& t);
template<typename T>
void g(T&& t)
{
f(forward<T>(t));
}
That's why it's called "forward", because you are "forwarding" the value category on, rather than losing it.
So in the example if you call g with an rvalue, then f will be called with an rvalue - rather than an lvalue.
It is used to preserve the exact type of an argument in templates when passing it to another function. For example:
template<class T>
void wrapper(T&& arg)
{
foo(std::forward<T>(arg)); // Forward a single argument.
}
This works as follows:
If the function wrapper gets a std::string or const std::string&, then foo is called as if arg has type of const std::string&.
If the function wrapper gets a std::string&, then foo is called as if arg has type of std::string&.
If the function wrapper gets a std::string&&, then foo is called as if arg has type of std::string&&.
The problem that std::forward solves is that by the usual rules the type of arg within function is std::string even if we pass std::string&& to wrapper. std::forward allows to inject the actual type of T, be it T, T&, const T& or T&&, to the call site.
It's basic use is you're in function g that has been called like this:
g(T1 p1, T2 p2, /* ... */);
and you want to call function f with exactly the same types:
f(T1 p1, T2 p2, /* ... */);

Overloaded function templates with reference parameters

template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
int main()
{
int x;
f(x); //ambiguous
}
Why is this call ambiguous? The first template specialization is f<int>(int&), and the second is f<int&>(int&). As the parameters are the same, the function template, which is more specialzed according to the partial ordering rules, is better. Then according to Standard 14.8.2.4/9
If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):
— if the type from the argument template was an lvalue reference and the type from the parameter template was not, the argument type is considered to be more specialized than the other; ...
The first template has T& and the second has T&&, so the first should be more specialized. What is wrong here?
Edit:
This code is tested in g++ 4.6.1 and VC++ 2010 Express, both give the ambiguous error.
Guideline:
Do not overload:
template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
Reason:
There is a special template deduction rule for the pattern:
template <typename T> void f(T&&) {}
This rule exists in order to enable so called "perfect forwarding". It helps things like bind and make_shared forward their arguments perfectly, preserving both cv-qualifiers and "value category" (lvalue/rvalue-ness).
This special rule says that when f(T&&) is called with an lvalue parameter (e.g. int), that T gets deduced as an lvalue reference (e.g. int&) instead of int. And an rvalue reference to lvalue reference to int collapses down to just lvalue reference to int. I.e.
f(x)
calls
f<int&>(int& && x);
which simplifies to:
f<int&>(int& x);
Edit
This is not more or less specialized than f<int>(int&).
Thanks to Johannes Schaub for the correction (see comments).
Solution:
You can do whatever you want with the single function:
template <typename T> void f(T&&) {}
If T deduces as an lvalue reference, do whatever you wanted to do in your first overload, otherwise do whatever you wanted to do in your second overload:
template <class T> void f_imp(T&, std::true_type) {std::cout << "lvalue\n";}
template <class T> void f_imp(T&&, std::false_type) {std::cout << "rvalue\n";}
template <typename T> void f(T&& x)
{
f_imp(std::forward<T>(x), std::is_lvalue_reference<T>());
}
And use std::forward to perfectly forward x to the implementation-detail function.
Your interpretation of the standard appears to be correct.
template <typename T> void f(T&) {} // #1
template <typename T> void f(T&&) {} // #2
In #1, T is successfully deduced as int, and in #2, T is successfully deduced as int&, so partial ordering is performed to select the function to call. During partial ordering for the call f(x), the types of the first (only, in this case) argument will be ordered ([temp.deduct.partial]/3 bullet 1). During deduction in both directions, type P will be T, and type A will be a synthesized type representing T ([temp.deduct.partial]/5), so deduction succeeds in both directions.
As you observed, [temp.deduct.partial]/9 then applies, and says that #1's first argument is more specialized. Therefore, by [temp.deduct.partial]/10, #1 is selected as the most-specialized template and its specialization is the result of overload resolution.
You didn't mention which compiler you are using. I assume it's g++ -- this appears to be a bug in that compiler (I've tested versions between 4.4.3 and 4.7, and they all reject this code). clang accepts your code, and calls the f(T &) overload as you expected.