I have some questions concerning function templates.
My plan was to build a wrapper which derives from a user-defined class and
not only exports the public functions of that class but also its constructors.
So I decided I would use multiple constructor templates (which I presume work exactly
the same as function templates) with 1 to n parameters to satisfy most constructors needs.
These would than simply call the constructor and do something else afterwards, like
this:
template <class T>
class Wrapper : public T
{
public:
template <class U>
Wrapper(U &u) : T(u) { doSomething(); }
template <class U, class V>
Wrapper(U &u, V &v) : T(u,v) { doSomething(); }
...
};
My intent is to register the instance within the Wrapper-Ctor somewhere else and,
from that point on, it can receive calls to virtual functions defined in T.
I had to use the reference operator in the code above, in order to guarantee that
my Wrapper-Ctor does not have any side-effects on the parameters that were passed
(copy-construction).
To my surprise this always worked, except for temporaries, which is the reason why
I am confused about the types that are inferred by the compiler in this situation.
To simplify the situation I tried to do something similiar via a template function:
template <class T>
void foo(T &t)
{
int x = ""; // intentional error
}
Calling the function like this:
std::string a;
std::string &b = a;
foo(b);
To my surprise the compiler denotes [T = std::string] in its error message.
I would have expected for this to be [T = std::string&], which would have caused
passing a reference-to-reference, which is invalid.
So, why does the compiler deduce a value-type in this situation?
Is it even possible to create a Wrapper-Ctor that does what I want, does not
have any side-effects on the parameters and also accepts temporaries?
Thanks alot!
It looks like the C++ spec explicitly states that this is the intended behavior. Specifically, if you have a template function that takes in a parameter P that depends on a template type argument, if P is a reference, then the underlying type of the reference, rather than the reference type, is used to determine what type should be used for P (see §14.8.2.1/2). Moreover, this same section says that const and volatile qualifiers are ignored during this step, so the constness can be inferred automatically.
It is not possible in C++03 to provide such a thing without manually overloading for every combination of const and non-const parameters.
No expression ever has reference type. Therefor, when argument deduction deduces against the argument expression type, it cannot make a distinction between a and b because the arguments a and b both have the same type.
Refer to [expr]p5 in the spec
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.
Somewhat late, but since I don't think this was answered completely...
For template parameter deduction, see the previous answers.
For your problem with temporaries, make the parameters const references (as in Wrapper(const U&)).
The thing is, temporaries are rvalues. The standard states that non-const references can only be bound to lvalues. Therefore, a standards compliant compiler won't let you pass temporaries(rvalues) as arguments to non-const reference parameters. (This doesn't have anything to do with templates in particular, it's a general rule).
This is to the best of my knowledge, so take it with a bit of scepticism.
Related
Lets consider ordinary perfect forwarding:
class Test
{
public:
Test() = default;
Test(Test const&) { std::cout << "copy\n"; }
Test(Test&&) { std::cout << "move\n"; }
};
void test(Test)
{ }
template <typename T>
void f(T&& t)
{
test(std::forward<T>(t));
}
int main()
{
std::cout << "expect: copy\n";
Test t;
f(t);
std::cout << "expect: move\n";
f(Test());
return 0;
}
So far everything is fine. But if we now introduce a function pointer I get the problem that I seem not to be able to declare universal (forwarding) references:
decltype(&f<Test>) ptr = &f<Test>;
// above produces ordinary r-value references
// making below fail already on compilation:
ptr(t);
Template function pointers get problematic as well:
template <typename T>
void(*ptr)(T&& t) = &f<T>; // again resolved to r-value reference
At first, they resolve to pure r-value reference as well, additionally they define a bunch of pointers instead of a single one, making the approach unusable within class scope:
class C
{
template <typename T>
void(*ptr)(T&& t); // fails (of course...)
};
So question now is: Is it possible at all to have indirect perfect forwarding via function pointers?
Admitted, already fearing the answer is 'no' (and currently falling back to l-value references), but still in hope of having overlooked something somewhere...
A forwarding reference is not only a hypothetical concept. It is the name given to a specific kind of rvalue reference which has special deduction rules in template argument deduction.
Specifically according to [temp.deduct.call]/3:
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). 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.
Except for this special rule (and one other in [temp.deduct.type]), a forwarding reference behaves just like any other rvalue reference.
In particular when providing a template argument for T, there is no template argument deduction happening and substitution is performed directly. In substitution the usual reference collapsing rules are applied.
So f<Test> will yield the function parameter Test&& which is a rvalue reference and f<Test&> will yield the function parameter Test& (collapsed from && being applied to Test&).
These are also the template arguments that would be deduced for a rvalue argument and a lvalue argument in a function call without explicit template arguments. If the reference was not a forwarding reference, then T could never be deduced to a reference and the function parameter would always be a rvalue reference after substitution. The special adjustment of A mentioned in the quote allows T to be deduced to an lvalue reference type, so that the collapsing rules will result in an lvalue reference function parameter as well.
Forwarding references can only exist in templates. A function or specialization of a template cannot have a forwarding reference. They are not somehow a different category of reference from rvalue/lvalue references. They work in templates only by deducing to different types for different value categories of arguments and produce distinct specializations for each value category.
So, since a function pointer must point to a function, not a function template, which of the two specializations for the value category of the argument to choose, has to be decided when taking the function pointer.
If deduction of any kind is expected, then a function pointer cannot offer that. Instead a lambda or functor type should be used which can perform deduction and can choose the function or function template specialization to call based on value category.
The first example uses template argument deduction and reference collapsing rules to compute the correct type. When you explicitly supply the template arguments this disables deduction and the supplied type is substituted directly, resulting in the rvalue reference.
Perfect forwarding requires the template argument to be deduced. A function pointer can only point to a single function, not to a set of overloads, and not to a function template.
You can get a function pointer to either the r-value reference instantiation:
decltype(&f<Test&&>) ptr1 = &f<Test&&>;
ptr1(Test()); // output: move
or the l-value instantiation:
decltype(&f<Test&>) ptr2 = &f<Test&>;
ptr2(t); // output: copy
But not to both at the same time. ptr1 and ptr2 are pointers to functions of different type.
When you want something to hold not just a single function but more than one overload you can use a type with member functions. For example with a lambda expression:
auto fw = [](auto&& t){ test(std::forward<decltype(t)>(t)); };
fw(t); // output: copy
fw(Test()); // output: move
Background of the question is a mis-reading of Scott Meyer's article about forwarding references (called 'universal references' there).
The article gave the impression of such forwarding references existing as a separate type in parallel to ordinary l-value and r-value references. This is not the case, though, instead it is a hypothetical construct to explain special deduction rules that exist if template parameters serve as function parameter types.
As such a type does not exist in consequence it is not possible to declare a function pointer using that type, proving indirect perfect forwarding via function pointer impossible to realise.
Here for example, b is of type int& but f(b) resolves to f(int):
#include <type_traits>
template <typename T>
void f(T arg) {
static_assert(std::is_reference<T>::value); // fails
}
void g() {
int a = 5;
int& b = a;
f(b);
}
I know the standard dictates it - I'm asking, why? What goes wrong (or - becomes 'surprising') if the reference isn't dropped?
There are two things at play here.
What if you, as the author of f, wants to write a template function where the user is required to copy/move a parameter into the function?
In non-template code, that prototype would look like this: void f(SomeType st);. Any caller of f has no choice but to copy/move into the parameter. Whether they have an object, a reference to an object, or something else. st shall be an object separate and distinct from the rest.
Template argument deduction is intended to look like the non-template version where possible. So if you have template<typename T> void f(T t);, and you call it with f(some_object), that should work exactly like the untemplated version.
The other thing at play here is that reference variables are supposed to just be a different name for some object. That is, a and b, should, all things being equal, behave identically. So f(a) ought to do the same thing as f(b).
The rules of template argument deduction favor both of these. It keeps references behaving exactly like the object, and it allows the template function to behave like an equivalently-defined non-template version.
If you want a reference type, you must forgo template argument deduction and specify it directly: f<decltype((b))>(b). Note the use of double-parentheses here; that's important for getting decltype to be a reference.
According to the rules of the language, f(b) is not calling f with an expression of type int&. It is calling f with an lvalue of type int. The same would also occur if you did f(a).
In general, when a reference variable is mentioned by name, it is an expression that is an lvalue of the referenced type. The fact that the variable itself is a reference is not visible to the type system.
Thus, f never deduces T to have reference type. From f's point of view, the argument is never actually a reference.
What goes wrong (or - becomes 'surprising') if the reference isn't dropped?
Consider following function:
template <typename T>
void f(T arg) {
something = std::move(arg);
// ...
f(lvalue); // copies
f(str::move(lvalue)); // moves
The intention is to copy from an lvalue, and move from an rvalue.
If T deduced to a reference, then lvalue argument would be moved from, which is in this case undesirable.
I don't want to copy the argument.
If you're writing the function, then specify a reference parameter using & symbol, and not an object parameter. That way the argument can never be copied into the call (you can still copy inside the function).
If you're calling a function that accepts an object parameter, then pass an rvalue and not an lvalue. That way you will copy only if the move is a copy, and there is no copy elision involved.
Doesn't make sense to me that the template type deduction rules decide for me that I do
You decided to use T. You should instead decide to use T&, T&& or const T& if you want a reference.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
template pass by value or const reference or…?
What is the good practice in the following for a function taking a function as a parameter :
template<class Function> void test1(Function f);
template<class Function> void test2(Function& f);
template<class Function> void test3(const Function& f);
where the passed function can be a functor, a std::function, a function pointer or a lambda function.
Use universal references and you won't need to think about it:
template<class Function> void test(Function&& f);
When an argument is passed to a function template deducing the argument's type, it is an interesting question as to how the argument should be passed. It is worth noting that the exact nature of the use of the argument doesn't matter: Whether the argument is used as a function object, an iterator, a value, etc. is fairly irrelevant to answer. There are four options:
template <typename T> void f(T&& a)
template <typename T> void f(T& a)
template <typename T> void f(T const& a)
template <typename T> void f(T a)
It matters a bit as to what the function template actually does: If all f() does with its argument a is to forward it to another function, you want to pass by universal reference. The called function will sort out the details an reject inappropriate options. Of course, although generally useful, forwarding functions are somewhat boring.
If f() actually does something with the object, we can normally immediately discard two of the options:
Pass by universal reference results in a type of which we don't know if it is a reference or a value. This is pretty useless for anything other than forwarding the type as it either behaves like a value or like reference. Just specifying what the function actually does would be a problematic dance.
Pass by T const& isn't doing us much good either: we got a reference to an object whose life-time we can't control and which we can neither move from nor get it copy/move-elided.
Passing an object by non-const reference can be useful if the object itself gets modified. This is clearly an important part of the contract of the function and an imposed design choice which can't be overridden by a client of the function when taken.
The preferred approach is to take arguments by value. Generally, this makes the definition of the function's behavior much easier and actually keeps the choice whether the type should follow value or reference semantics open for the user! Just because something is passed by value doesn't mean that the objects of interest are also passed by value. Specifically for the case of function object, the standard C++ library even provides a generic adapter giving a value type reference semantics: std::ref().
Of course, interface design is subtle and there will be cases where each one of the options is warranted. However, as a general rule of thumb, I think it is very simple:
Forwarding functions use universal references.
Functions doing something with the arguments use values.
... and, of course, these rules only apply to arguments to function templates whose type is deduced.
I've been playing around with C++0x's auto keyword and tried the following.
std::unique_ptr<auto> ptr(new int(0));
I tried compiling it with g++ 4.4.5 and got
error: invalid use of auto
Judging by eye, auto can easily be inferred to int.
My guess is the type inference and the template engine don't talk to each other. Otherwise, the template engine would know to instantiate the template class with int as the type parameter.
Another guess is from the standard, I see this.
A member shall not be declared with auto, extern or register storage class.
But I thought that was the auto as in local variables, not as in auto used to deduce types.
And my last guess is that the compiler thinks this is an auto storage class, not auto for type deduction.
Is there a reason behind this stated in the standard?
That's because it has to determine the class on which to call a constructor before determining what to do with its arguments. If you make the constructor a template, it'll just work like any other template function - auto-deducing arguments.
#dascandy has correctly identified what's wrong with your code. I'll try to provide some rationale:
You're expecting the compiler to infer unique_ptr<int> because the argument is an int*, and unique_ptr<int> has a constructor which accepts int*. For a moment let's ignore the fact that we're using std::unique_ptr, and just talk about a template class we wrote (and can specialize).
Why should the compiler infer unique_ptr<int>? The argument isn't int, it's int*. Why shouldn't it guess unique_ptr<int*>? Of course that would result in a compiler error, since unique_ptr<int*>'s constructor won't accept an int*. Unless I add a specialization:
template<>
class unique_ptr<int*>
{
public:
unique_ptr(int*) {}
};
Now unique_ptr<int*> would compile. How should the compiler know which to choose, unique_ptr<int> or unique_ptr<int*>? What if I add another specialization?
template<>
class unique_ptr<double>
{
public:
unique_ptr(int*) {}
};
The compiler now has three options to choose from, and it has to instantiate the template with every possible argument in order to find them. Clearly this is not feasible, especially with multiple template arguments and template recursion.
What you can do, is make a factory function which connects the inferred type to exactly one template instance:
template<typename T>
std::unique_ptr<T> make_unique(T* arg) { return arg; }
(of course, this won't work because unique_ptr cannot be copied. But the idea is valid, and used in e.g.make_shared and make_pair.)
Some examples of extreme ugliness:
One could argue that unique_ptr<shared_ptr<int>> is a valid match for this code.
Or how about:
template<typename T>
class unique_ptr
{
public:
explicit unique_ptr(T* arg);
unique_ptr(int*, enable_if<(sizeof(T) > 16)>::type* = 0);
};
Just want to add that a solution already exists for most cases:
template <typename T>
std::unique_ptr<T> unique_ptr_auto(T* ptr)
{
// fails to handle std::unique_ptr<T[]>, not deducible from pointer
return std::unique_ptr<T>(ptr);
}
auto ptr = unique_ptr_auto(new int(0));
A bit more verbose, obviously, but you get the idea. These "generator functions" are quite common.
This (or similar) was proposed for the Standard. The proposed functionality looked something like:
std::vector<int> GetMahVector();
std::vector<auto> var = GetMahVector();
However, it was rejected. Why it was rejected, well, you'd have to dig up the relevant Standard process documents, if possible.
template<typename T> T* Push(T* ptr);
template<typename T> T* Push(T& ref);
template<typename T, typename T1> T* Push(T1&& ref);
I have
int i = 0;
Push<int>(i);
But the compiler calls it ambiguous. How is that ambiguous? The second function is clearly the preferred match since it's more specialized. Especially since the T1&& won't bind to an lvalue unless I explicitly forward/move it.
Sorry - i is an int. Otherwise, the question would make no sense, and I thought people would infer it since it's normally the loop iterator.
If i is an int, then the first isn't viable. Last two remain. Then, for deduction of i, the second and the third both yield the same function types for overload resolution (both int& as parameter). So you have to rely on partial ordering.
However, partial ordering can't tell them apart. For a function call partial ordering context, only the parameters are used to determine an order (and the return type in your example is not considered), and any reference modifier is peeled off from them. So you will succeed deducing the parameter type from one against the other in both direction - both parameter types will be at least as specialized as the other parameters respectively. And neither has const applied, so neither is more specialized than the other.
There is an issue report placeholder that aims at clarifying anything related to rvalue/lvalue reference difficulties during partial ordering. See this usenet question for details.
If any of the two should be more specialized, i would say that it should the first one. After all, it accepts less arguments than the other one (the other one being a potential perfect forwarder).
Especially since the T1&& won't bind to an lvalue unless I explicitly forward/move it.
Actually, it will accept anything. Having a parameter of type T&& in a template will switch to the "perfect-forwarding-deduction-mode", which will deduce T to the type of the argument if it's an rvalue, and add a lvalue-reference modifier to the type of T if it's an lvalue. So if the argument is an lvalue, the resulting parameter type is T& && collapsed to T&, which accepts lvalues fine (just like in your case).
On a second look, what you seem to be trying to do is to overload a function for taking objects by moving them. But this won't work because of the special deduction done for T&& (see below). Just erase the first function and write your code as
template<typename T, typename T1> T* Push(T1&& ref) {
/* for lvalues, T1 is U& and rvalues it is U, with U being the
* argument type. */
T t1(std::forward<T1>(ref));
/* whatever needs to be done ... */
}
This will move-construct t1 if the argument was an rvalue, and copy ref if the argument was an lvalue or if T doesn't have a move constructor. This is just an illustration, it may not be what you actually should do depending on your real use-case. I'm also not sure why you have two template parameter types here. I propose to get rid of the T, and say typename remove_reference<T1>::type * for the return type, instead. So that you can gain from argument deduction.