Suppose you have a template argument T.
What are the differences between
add_cv_t<T> and const volatile T
add_const_t<T> and const T
add_volatile_t<T> and volatile T
add_lvalue_reference_t<T> and T&
add_rvalue_reference_t<T> and T&&
add_pointer_t<T> and T*?
Why should I use add_rvalue_reference_t<T> instead of T&& for example. Are there any rules when to choose which?
add_cv_t<T> and const volatile T
add_const_t<T> and const T
add_volatile_t<T> and volatile T
No difference; the definition of add_const<T>::type is just T const, for example.
add_lvalue_reference_t<T> and T&
add_rvalue_reference_t<T> and T&&
T& and T&& are ill-formed when T is cv void, but these templates are well-formed, just giving the original type back.
add_pointer_t<T> and T*?
add_pointer_t<T> is equivalent to std::remove_reference<T>::type*. That is, if T is a reference type, it gives a pointer to the referenced type. On the other hand, T* will be ill-formed since you cannot have a pointer to a reference.
Which should you use?
In general, the alias templates can be used to prevent deduction of T. Of course, that means that if you want deduction, you should avoid them.
The alias templates can be used as template template arguments to a template that takes a type transformation as a parameter.
The alias templates that differ in behaviour from alternatives like T* are useful in generic code since they "do the right thing". For example, if T is deduced from an argument of type T&&, then T* does the wrong thing when the argument is an lvalue, since it tries to declare a pointer to an lvalue reference. But std::add_pointer_t<T> will give a pointer to the actual type of the argument.
According to what I see in STL source:
add_cv_t<T> and const volatile T- no difference
add_const_t<T> and const T - no difference
add_volatile_t<T> and volatile T - no difference
add_lvalue_reference_t<T> and T& - there is difference for example if T is non referenceable type void. add_lvalue_reference_t<void>::type = void and void& = compile-time error
add_rvalue_reference_t<T> and T&& - the same as above
add_pointer_t<T> and T* - difference when T is reference, because there is no such thing as pointer to reference. add_pointer_t<T> is equivalent to std::remove_reference<T>::type*
In most cases, std::add_rvalue_reference_t<T> is equivalent to T&&. However, reference collapsing rules and the-rules-that-dictate-which-types-are-referenceable may have your code misbehave if not taked into account.
There are, however, some cases where the type static member type will be different due to T being a non-referenceable type. For example std::add_rvalue_reference_t<void> resolves to void, and (taking another template you mentioned as an example) std::add_pointer_t<T&> resolves to T* (if you want to invoke chaos, the required ritual is std::add_pointer_t<std::add_rvalue_reference_t<void>> :))
Respecting to uses, it may be used as a template template parameter to do some funky black magic. Anyway, stuff like std::is_rvalue_reference_t<T> or std::remove_reference_t<T> is usually more commonly used when manipulating the type's reference attributes.
Related
To make a concept checking if a type can be converted without narrowing to another, it is proposed here to make it using std::forward and std::type_identity_t like this:
template<class T, class U>
concept __construct_without_narrowing = requires (U&& x) {
{ std::type_identity_t<T[]>{std::forward<U>(x)} } -> T[1];
};
I understand from it why something like this:
To{std::declval<From>()}
gives incorrect results, but when i try to simplify it using another idea in the paper, writing just
template <typename From, typename To>
concept WithoutNarrowing =
requires (From x) {
{(To[1]){x}}
->std::same_as<To[1]>;
};
It seems to give the same results. What circumstances have to occur for it to give different result? Or is it equivalent? For what reason is std::forward used here?
This is the usual approach for type traits like this that involve some kind of function/constructor argument.
U is the type from which T is supposed to be constructed, but if we want to discuss the construction we also need to consider the value category of the argument. It may be an lvalue or a rvalue and this can affect e.g. which constructor is usable.
The idea is that we map the rvalue argument case to a non-reference U or rvalue reference U and the lvalue argument case to a lvalue reference U, matching the mapping of expressions in decltype and of return types with value categories in function call expressions.
Then, by the reference collapsing rules, U&& will be a lvalue reference if the constructor argument is a lvalue and otherwise a rvalue reference. Then using std::forward means that the actual argument we give to the construction will indeed be a lvalue argument when U was meant to represent one and a rvalue argument otherwise.
Your approach using {(To[1]){x}} doesn't use the forwarding and so would always only test whether construction from a lvalue can be done without narrowing, which is not what is expected if e.g. U is a non-reference.
Your approach is further incorrect because (To[1]){x} is not valid syntax in standard C++. If X is a type you can have X{x} or (X)x, but not (X){x}. The last syntax is part of C however and called a compound literal there. For that reason a C++ compiler may support it as an extension to C++. That's why the original implementation uses the round-about way with std::type_identity_t.
The implementation seems to also be written for an earlier draft of C++20 concepts. It is now not possible to use types to the right of -> directly for a requirement. Instead a concept, i.e. -> std::same_as<T[1]>, must be used as in your suggested implementation.
well there is difference between (U u), (U& u) and (U&& u) that std::forward is supposed to preserve. in case of (U u) the type has to have defined a copy constructor (since (U u) basically means "pass a copy of")
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.
I've been reading Effective Modern C++ and the following thing caught my attention:
In Item 28 Scott writes:
Together, these observations about universal references and
lvalue/rvalue encoding mean that for this template
template<typename T> void func(T&& param);
the deduced template parameter T will encode
whether the argument passed to param was an lvalue or an rvalue. The
encoding mechanism is simple. When an lvalue is passed as an argument,
T is deduced to be an lvalue reference. When an rvalue is passed, T is
deduced to be a non-reference. (Note the asymmetry: lvalues are
encoded as lvalue references, but rvalues are encoded as
non-references.)
Can somebody explain why such encoding mechanism was chosen?
I mean if we will follow reference collapsing rules than usage of aforementioned template with rvalue yields rvalue reference. And as far as I can tell everything would work just the same if it were deduced as rvalue reference. Why is it encoded as non-reference?
Let's say you need to pass param around. In fact, you need to store it for lifetime purposes. As is, you'd just use T:
template <typename T>
struct store { T val; };
template<typename T> void func(T&& param) {
store<T> s{std::forward<T>(param)};
}
This works because if param got passed in by lvalue, T would be an lvalue reference type, and we're just copying the reference. If param got passed in by rvalue, we need to take ownership - T is a non-reference type, so we end up move-constructing into s.
The fact that I can just use T here as the template argument for store, instead of std::conditional_t<std::is_lvalue_reference<T>::value, T, std::remove_reference_t<T>> is probably not an accident.
I think you're thinking the wrong way around. Instead of asking "why not always make T a reference type", look at it from the other side:
Suppose you have template <typename T> void f(T&&t).
Calling it as int i; f(i);, some T is needed such that T&& resolves to int&. What's the simplest T that can achieve that? It's int&.
Calling it as f(0);, some T is needed such that T&& resolves to int&&. What's the simplest T that can achieve that? It's int, not int&&.
int&& simply doesn't really have any advantages here.
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.
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.