I have 2 overloaded functions - one takes an L-value, and the other takes an R-value. The purpose is so that the function can be called like:
Obj obj;
foo(obj);
OR:
foo(Obj());
So, I write 2 overloaded functions:
template <class T>
void foo(T& v)
{
/* ... function body code goes here ... */
}
template <class T>
void foo(T&& v)
{
foo(v);
}
int main()
{
foo(int(5));
}
The R-value overload merely needs to delegate to the L-value overload. The way I understand it, once I'm in the body of the function, any use of v gives me an L-value reference, unless I specifically use std::move or std::forward. So calling foo(v) within the R-value overload should automatically call the L-value version (rather than recursing.)
But, the compiler complains about ambiguity:
test.cpp: In function ‘void foo(T&&) [with T = int]’:
test.cpp:305:12: instantiated from here
test.cpp:299:2: error: call of overloaded ‘foo(int&)’ is ambiguous
I don't understand why this is ambiguous. The call to foo() within the R-value overload should clearly call the L-value version. So why doesn't this compile?
Short version: Try updating your compiler. Your version doesn't implement http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1164 .
Your second template is a "perfect forwarding" template. Any function template parameter of type T&&, where T is a template parameter will deduce that template parameter to X (where X is the argument type) when the argument is an rvalue, and to X& if the argument is an lvalue.
In your case you passed an rvalue, so T was deduced to Obj (and int in your real code). If you would have passed a variable name or something else that is an lvalue, your second template would have the parameter type Obj& (T would be Obj&, and && applied to such a type stays Obj&).
But the other template also has such a parameter type. So during overload resolution the conversion of the argument to the parameter is the same (perfect match), and another criteria needs to inspected, the specifity of the two templates, under the partial ordering rule. If one template is more specialized than the other template, then it will be chosen by the compiler. If there is no template more specialized than the other one, a final ambiguity is risen.
In this case, the first template is more specialized than the second template, and hence the compiler should call the first template finally.
Related
How did compiler decide to call bar function without knowing the type of template parameter T of foo function?
Usually when we call foo(2), T is deduced as int based on argument 2.
Here T is deduced based on parameters of function bar to which foo is passed.
#include <iostream>
template<typename T>
void foo(const T& a_) {
std::cout << a_<< std::endl;
}
void bar(void (*ptr) (const int&)) {
std::cout << "bar called" << std::endl;
}
int main() {
bar(foo);
}
compiler: gcc
When you have an overload set like foo, meaning that the result of name lookup for foo results in (potentially multiple) non-template functions or function templates and the overload set has its address or a reference taken (here implicitly by passing it as a function argument), then the compiler will try to do overload resolution against the target type if there is any. The target type here is void(*)(const int&). The whole procedure is specified in [over.over], parts of which I explain below.
The way this works is that the compiler will look at each function and function template in the overload set, will look whether it can match its type against the target type and will then add those functions and function template specializations that match to a set of selected overloads. Afterwards a few more elimination steps are done to reduce the set of selected overloads (e.g. checking whether associated constraints are satisfied and preferring functions over function template specializations, see [over.over]/5 for all details) and hopefully at the end exactly one selected function or function template specialization remains, which will then be chosen as the result of overload resolution that the original name (foo) refers to and from which the address/reference is taken.
For a non-template function a match means that the function type of the target ( void(const int&)) needs to simply be identical to the function's type. But you don't have any non-template overload here.
For a function template the compiler will try to perform template argument deduction to select one specialization of the template that matches the type and can be added to the list of selected overloads. Specifically this works just the same as in template argument deduction of a function call where there is usually a list of function argument/parameter pairs to deduce template arguments from, but in this case (given that there is a target type) the deduction will consider only one argument/parameter pair with the argument being the target type (void(*)(const int&)) and the parameter being the function type of the template containing the template parameters, i.e. here void(const T&), adjusted to a pointer type void(*)(const T&) because the argument is a function pointer type.
So in the end the compiler will perform normal deduction of
void(*)(const int&)
against
void(*)(const T&)
to decide what T should be in the selected specialization. I guess it is pretty obvious that T should be int for the types to match. So the selected specialization will be the one with T = int which has a function type matching the target type and since it is the only selected overload and since it will not be eliminated in any of the further steps, it will be chosen as the result of overload resolution.
It actually deduces the parameter of T from the type of function pointer, how this basically works.
At first compiles sees that you called bar, which accepts function pointer which returns void and takes one argument of const int& type.
Then it sees that passes parameter is function template. Then it starts to wonder if you can instantiate the function so that it's return type and parameter list is in correspondence with whatever the function pointer type is.
Voila. The compiler indeed can achieve this by removing cv qualifiers and references. The T appears to be int.
template <typename T>
void fun1(T t) {}
template <typename T>
void fun2(T && t) {}
int i = 1;
fun1(i); // the deduced type of T is int
fun2(i); // the deduced type of T is int &
The deduced type of T in fun1(i) and fun2(i) are int and int & respectively, can anyone explain the mechanism how compiler do deduction?
UPDATE
This question is not a duplicate of Type not deduced to be r-value reference: why not?, because:
The later question explained the deduction rules for :
template <class T>
void foo(T&& )
Here, I want to know the difference of deduction rules for
template <class T>
void foo(T&& )
and
template <class T>
void foo(T )
That is aperfect forward which can produce different outcomes depending on what arguments are provided to the template.
Perfect forwarding reduces the need for overloaded functions and helps
avoid the forwarding problem. The forwarding problem can occur when
you write a generic function that takes references as its parameters
and it passes (or forwards) these parameters to another function. For
example, if the generic function takes a parameter of type const T&,
then the called function cannot modify the value of that parameter. If
the generic function takes a parameter of type T&, then the function
cannot be called by using an rvalue (such as a temporary object or
integer literal).
Ordinarily, to solve this problem, you must provide overloaded
versions of the generic function that take both T& and const T& for
each of its parameters. As a result, the number of overloaded
functions increases exponentially with the number of parameters.
Rvalue references enable you to write one version of a function that
accepts arbitrary arguments and forwards them to another function as
if the other function had been called directly.
Rvalue Reference
Forwarding references
Forwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward. Forwarding references are either:
function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template:
auto&& except when deduced from a brace-enclosed initializer list.
Forwarding Reference
How it is donde is explained here:
Template Deduction
Because the type of i is int, not int&. As [temp.deduct.call] paragraph 1 says
Template argument deduction is done by comparing each function template
parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below.
And [temp.deduct.call] paragraph 4 says
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above).
The forwarding reference case is a special case. As [temp.deduct.call] paragraph 3 explicitly says
If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
template <typename T>
void func(T&){
}
int main(){
void (*p)(int&) = func;//or &func
return 0;
}
I wonder why this code compiles (with g++). It seems the argument of template function is deduced from the type of p? Is this standard behavior?
Edit:
I came up with a possible explanation. That assignment has signature:
void(*&)(int&)operator=(void(*)(int&));
So func is actually deduced from the input argument type of operator=, rather than from type of p directly. Is that correct?
Is this standard behavior?
Yes it is. Template argument deduction also happens when you take the address of a function template (such as you do when assigning to or initializing a function pointer). It's explicitly allowed in [temp.deduct.funcaddr]/1:
Template arguments can be deduced from the type specified when taking
the address of an overloaded function. The function template's
function type and the specified type are used as the types of P and A,
and the deduction is done as described in [temp.deduct.type].
The function pointer type provides the argument (A in the above paragraph).
So func is actually deduced from the input argument type of operator=, rather than from type of p directly. Is that correct?
Not really. For one, it's not assignment, it's initialization that you are doing. And even if it was using an overloaded operator= function, you'd need deduction to initialize the argument for the assignment operator, which brings you back to square one.
Consider this snippet of code, which uses the common idiom of having a function template construct an instance of a class template specialized on a deduced type, as seen with std::make_unique and std::make_tuple, for example:
template <typename T>
struct foo
{
std::decay_t<T> v_;
foo(T&& v) : v_(std::forward<T>(v)) {}
};
template <typename U>
foo<U> make_foo(U&& v)
{
return { std::forward<U>(v) };
}
In the context of Scott Meyers' "universal references", the argument to
make_foo is a universal reference because its type is U&& where U is
deduced. The argument to the constructor of foo is not a universal reference
because although its type is T&&, T is (in general) not deduced.
But in the case in which the constructor of foo is called by make_foo, it
seems to me that it might make sense to think of the argument to the constructor
of foo as being a universal reference, because T has been deduced by the
function template make_foo. The same reference collapsing rules will apply
so that the type of v is the same in both functions. In this case, both T
and U can be said to have been deduced.
So my question is twofold:
Does it make sense to think of the argument to the constructor of foo as being
a universal reference in the limited cases in which T has been deduced
within a universal reference context by the caller, as in my example?
In my example, are both uses of std::forward sensible?
make_foo is in the same ballpark as "right", but foo isn't. The foo constructor currently only accepts a non-deduced T &&, and forwarding there is probably not what you mean (but see #nosid's comment). All in all, foo should take a type parameter, have a templated constructor, and the maker function should do the decaying:
template <typename T>
struct foo
{
T v_;
template <typename U>
foo(U && u) : v_(std::forward<U>(u)) { }
};
template <typename U>
foo<typename std::decay<U>::type> make_foo(U && u)
{
return foo<typename std::decay<U>::type>(std::forward<U>(u));
}
In C++14 the maker function becomes a bit simpler to write:
template <typename U>
auto make_foo(U && u)
{ return foo<std::decay_t<U>>(std::forward<U>(u)); }
As your code is written now, int a; make_foo(a); would create an object of type foo<int &>. This would internally store an int, but its constructor would only accept an int & argument. By contrast, make_foo(std::move(a)) would create a foo<int>.
So the way you wrote it, the class template argument determines the signature of the constructor. (The std::forward<T>(v) still makes sense in a perverted kind of way (thanks to #nodis for pointing this out), but this is definitely not "forwarding".)
That is very unusual. Typically, the class template should determine the relevant wrapped type, and the constructor should accept anything that can be used to create the wrapped type, i.e. the constructor should be a function template.
There isn't a formal definition of "universal reference", but I would define it as:
A universal reference is a parameter of a function template with type [template-parameter] &&, with the intent that the template parameter can be deduced from the function argument, and the argument will be passed either by lvalue reference or by rvalue reference as appropriate.
So by that definition, no, the T&& v parameter in foo's constructor is not a universal reference.
However, the entire point of the phrase "universal reference" is to provide a model or pattern for us humans to think about while designing, reading, and understanding code. And it is reasonable and helpful to say that "When make_foo calls the constructor of foo<U>, the template parameter T has been deduced from the argument to make_foo in a way that allows the constructor parameter T&& v to be either an lvalue reference or an rvalue reference as appropriate." This is close enough to the same concept that I would be fine moving on to the claim: "When make_foo calls the constructor of foo<U>, the constructor parameter T&& v is essentially a universal reference."
Yes, both uses of std::forward will do what you intend here, allowing member v_ to move from the make_foo argument if possible or copy otherwise. But having make_foo(my_str) return a foo<std::string&>, not a foo<std::string>, that contains a copy of my_str is quite surprising....
I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}