An object is a region of storage that can be examined and stored into. 2 days ago · C++ Operator Overloading [ ] for lvalue and rvalue. So, clearly the value ’8′ in the code above is an rvalue. Compiled with "g++ -std=c++0x". You would need const_cast<char*&> (a) in order to have an lvalue to assign to, and that brings up the next problem. The array to pointer conversion occurs in most uses of an array in an expression, however, and so might surprise some people. Now an lvalue reference is a reference that binds to an lvalue. Correct, the epxression T() is always an rvalue for scalar and user-defined types T. uint8Vect_t encodeData(uint8Vect_t &dataBuff); Here you are taking a reference to a uint8Vect_t. , buggy). rvalue (until C++11) / prvalue (since C++11)Since you are giving your rvalue reference a name in the parameter list, it indeed becomes an lvalue. int&& x = 3; x is now an lvalue. by unconditionally casting its argument—which might be an lvalue—to an rvalue reference, it enables the compiler to subsequently move, rather than copy, the value passed in Arg if its type is. 97 * @brief Convert a value to an rvalue. The discussion of reference initialization in 8. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue. I am trying to figure out the meaning of the following snippet: int main() { int&& a = 2; int& b = a; // (*) } I know a is an lvalue expression of type "rvalue reference to int", and b is a general variable with type "lvalue reference to int". Ternary conditional operator will yield an lvalue, if the type of its second and third operands is an lvalue. This is indeed a temporary materialization; what happens is that the compiler performs lvalue-to-rvalue conversion on t2 (i. init. Write a function template to convert rvalues to lvalues: template<typename T> T &as_lvalue (T &&val) { return val; } Now, use it: deref (&as_lvalue (42)); Warning: this doesn't extend the lifetime of the temporary, so you mustn't use the returned reference after the end of the full-expression in which the temporary was. Yes, rvalues are moved, lvalues are copied. In short: every named object is Lvalue, and even if v is reference to Rvalue you need to use move to force move ctor to be called. Otherwise your compiler will throw an error: obj & a1 = bar (); invalid initialization of non-const reference of type ‘obj&’ from an rvalue of type ‘obj’. Convert enum class values into integers or floating-point values. 6 — Pass by const lvalue reference. We're talking about the temporary object created by Contrived(), it doesn't make sense to say "this object is an rvalue". I checked the C++ standard, and it clearly states that (clause 3. (An xvalue is an rvalue). Consequently, it's not legal to apply the ++ operator to the. But when there's no according move operation, rvalues are copied as well. Lvalue to rvalue conversion A glvalue of any non-function, non-array type T can be implicitly converted to a prvalue of the same type . Deciding whether a function must take an argument by value, lvalue reference or rvalue reference depends very much on what it does. returning either a rvalue or an lvalue. As long as no const is involved, the expression T() is a modifiable rvalue, to be more precise. A lvalue overload can accept both lvalues and rvalues, but an rvalue overload can only accept rvalues. — even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter. This approach is hard to generalize to more input arguments. and includes the following bullet which the examle belongs to: the evaluation of e results in the evaluation of a member ex of the set of potential results of e, and ex names a variable x that is not odr-used by ex (3. But you might just let regular deduction occurs. This is already done in some places. 10): An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. Safe downcast may be done with dynamic_cast. e. 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。. The type after conversion is not qualified by either const or volatile. If element at this position doesn't exist, function. An lvalue can be converted to an rvalue. An lvalue-to-rvalue conversion (converting the name of the object x to its value 2. 3) If new_type is an rvalue reference type, static_cast converts the value of expression to xvalue. h, it's seems that the difference between Clang and G++ is internally. rvalues are defined by exclusion. [ Note: If T is a non-class type that is cv. lvalueはアドレスを取得できるがrvalueはアドレスを取得できない。 これは一見見分ける強力な手段に思える。しかし考えて欲しい。コードを書くときにいちいちアドレス演算子を書いてコンパイルしてみるなんて悠長なことをするだろうか、いいやしない。2 Answers. having an address). c++11标准基本上是通过举例来说明一个表达式是否是一个lvalue还是rvalue的。. This differs from ISO C, in. Sorted by: 17. If the function argument is an rvalue, the compiler deduces the argument to be an rvalue reference. str is a rvalue reference, i. In particular, only const_cast may be used to cast away (remove) constness or volatility. class XAttr : public AttrDec { public: XAttr (const std::wstring& name) :AttrDec (new Attr (name)) // create a pointer here {} }; And then get rid of the rvalue constructor in AttrDec. 1) modifiable lvalues. In the op's example y is actually a reference to the sub-object of some unnamed object the structured binding declared. e. The answer lies in the second property of expressions: the value category. The goal of rvalue references is sparing copies and using move semantics. (until C++11) When an lvalue-to-rvalue conversion is applied to an expression E, the value contained in the referenced object is not accessed if: In general, lvalue is: Is usually on the left hand of an expression, and that’s where the name comes from - “left-value”. 4. You can use the function template is_lvalue (below) to find out if an operand is an lvalue and use it in the function template isTernaryAssignable to find out if it can be assigned to. (Lvalue-to-rvalue conversions on class types are rare, but do occur in some places in the language, e. lvalue-- an expression that identifies a non-temporary object. g++ t. You are returning a copy of A from test so *c triggers the construction of a copy of c. The C++17 standard defines expression value categories as follows: A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function. If an lvalue-to-rvalue conversion from an incomplete type is required by a program, that program is ill-formed. The r-value reference is a reference to the original object, so converting it to a l-value reference will just make a reference to the original object. No temporary is created, no copy is made, no constructors or. From C++11 4. 18. This example might clarify it:So we have a reference being initialized by an xvalue of type const foo. It is a forwarding reference. A move constructor and move assignment operator can now. h and move. The Lvalue refers to a modifiable object in c++ that can be either left or right side of the assignment operator. The biggest difference between a C++03 reference (now called an lvalue reference in C++11) is that it can bind to an rvalue like a temporary without having to be const. If you wanted to move an rvalue, you’re in luck!14. To convert an rvalue to an lvalue, you can use this lvalue helper function: template<class T> T& lvalue_ref (T&& x) { return x; } And then the call becomes: scan (lvalue_ref (std::ifstream ("myfile")), lvalue_ref (Handler ())); This is safe as the temporaries (the ifstream and Handler) aren't destructed until the end of. c++ c++11 overload-resolution rvalue Share Follow edited Jan 14, 2016 at 8:52 ildjarn 62. That is the historical origin of the letters l. Among. Note: The ISO C standard does not require this, but it is required for POSIX conformance. Then std::forward<SomeClass&> (element) will be invoked, and the instantiation of std::forward would be. However what matters here is the expression and: Each C++ expression (an operator with its operands, a literal, a variable name, etc. 10/7 reads, Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue; see 4. Per paragraph 8. So when. It's long-lived and not short-lived, and it points to a memory location where 1 is. In return w, the implicitly movable entity w is treated as an rvalue when the return type of the function is RRefTaker as in example three, but it is treated as an lvalue when the return type of the function is Widget && as in example four. Taking it by rvalue reference would cause a headache to a user who has an existing lvalue or const reference to a function; they would need to std::move it (in. This means the following is illegal: int main() { const int x { 5 }; int& ref { x }; return 0; } This is disallowed because it would allow us to modify a. The reason is simple; named rvalue reference is treated as lvalue (and implicit conversion from lvalue to rvalue reference is forbidden by standard). So a and b are converted to rvalues before getting summed. 区分左值和右值是很重要的,这是使用C++11 move语义的基础。. Why?The C++ standard specifies that such expressions do not undergo lvalue to rvalue conversion, and that the type of the dereferenced object may be incomplete. C++ does not allow you to get an r-value reference to a variable without an explicit conversion. )In the third line, they undergo an implicit lvalue-to-rvalue conversion. 1 Answer. However, a (prvalue) rvalue cannot be converted implicitly to an lvalue or xvalue, except by user-defined conversions. static_cast<typename remove_reference<T>::type&&> (t) The result of the function call is an rvalue (specifically, an xvalue ), so it can be bound to an rvalue reference where the function argument couldn't. std::move is there to allow for the casting. You need to pass in an rvalue, and for that you need to use std::move: Insert(std::move(key), Value()); // No compiler error any more I can see why this is. If type is an lvalue reference type or an rvalue reference to a function type, the cast result is an lvalue. Alex November 11, 2023. Variables are lvalues, and usually variables appear on the left of an expression. Their very nature implies that the object is transient. e. 3. To set this compiler option in the Visual Studio development environment. e. Rvalue references enable you to distinguish an lvalue from an rvalue. 2. Category 4 used to be a bit different in C++11, but I believe this wording is correct for C++14. B. It can convert lvalues to lvalue references and rvalues to rvalue references. The rvalue reference is bound to the temporary materialized from the prvalue conversion of arr. 8. Found workaround how to use rvalue as lvalue. Class rvalues prvalues]. Practically every example of lvalue-to-rvalue conversion I've seen on the web relates to fundamental types like int etc. So sizeof (0, arr) = sizeof (arr) and which would be equal to 100* sizeof (char) and not = sizeof (char*). To convert an lvalue to an rvalue, you can also use the std::move() function. The returned lvalue will contain exactly the result it is supposed to. See note at the end of this answer. Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i. If inside foo no move operation happened like my example, then my_ptr_var will not actually be moved from. A nice feature of this heuristic is that it helps you remember that the type of an expression is independent of. But in this particular case, the rules. Both of these options are user-defined conversion functions, so neither is better in terms of overload resolution, thus an ambiguity. 1) Two possibly multilevel pointers to the same type may be converted between each other, regardless of cv-qualifiers at each level. must error, because you're trying to pass an rvalue argument, std::move(n), to a lvalue reference parameter, T&. One more step. If the type is a placeholder for a deduced class type, it is replaced by the return type of the function. cast (this is applicable from C++11 and later). The reference could be bound to the result of the implicit conversion if it wasn't non-const because the result of that implicit conversion is an rvalue i. It was introduced specifically to allow temporary streams to be usable without resorting to tricks. A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. This implies that the compilers that accept the above code without a diagnostic are non-conforming (i. int rVal () { return 0; }. One could also say that an rvalue is any expression that is not an lvalue . And an rvalue reference is a reference that binds to an rvalue. int array [10]; int * p = array; // [1] The expression array in [1] is an lvalue of type int (&) [10] that gets converted to an rvalue of type int *p, that is, the rvalue array of N==10 T. As with all cast expressions, the result is: an lvalue if target-type is an lvalue reference type or an rvalue reference to function type(since C++11) ; an xvalue if target. the name of a variable, a function, a template parameter object (since C++20), or a data member, regardless of type, such as std::cin or std::endl. It can convert lvalues to lvalue references and rvalues to rvalue references. There is no lvalue-to-rvalue conversion in this scenario. 10) of a non-function, non-array type T can be converted to a prvalue. in . rvalue/lvalue tells you the value category. A compiler can optimize the call to copy constructor and directly call the matching constructor. template <typename element, unsigned int size> class array { private. " So an rvalue is any expression that is not an lvalue. According to the C++ specifications, it takes two rvalues as arguments and returns an rvalue. The lvalue to rvalue conversion isn't being done either, of course, but that's rather intuitive and normal. 2 1). For example, this means, that when rvalue reference is passed to a function, an lvalue reference overload will be chosen: T&& x=T(); f(x); Links: C++ lvalue rvalue xvalue glvalue prvalue Value categories in C++ 17 Value categories. When being passed an lvalue, the template parameter would be deduced as lvalue-reference, after reference. Lvalues and Rvalues. The type of b is an rvalue reference to int , but the expression b is an lvalue; it is a variable, you can take its address. 12. move simply returns an rvalue reference to its argument, equivalent to. std::move() is a function used to convert an lvalue reference into the rvalue reference. You would then need to add a destructor to AttrDec and delete the pointer in it and add a copy constructor. [dcl. But due to the the existence of std::vector::push_back(value_type const & val), which copies and would be the overriding call, I need to convert the lvalue object to an rvalue. conv] 1 A simple-type-specifier or typename-specifier followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer. The list of languages that are currently supported includes C++, C#, Go, Java, Kotlin, PHP, Python, Ruby, Rust, TypeScript, and more. The address of operator (&) requires an lvalue because you can only take the address of something in memory. I have tried to simulate the assignment of the object (pair. In fact, that's the origin of the names: an lvalue was (originally) anything that could appear on the Left side of an assignment, and. Share. This would seem to be possible since there is a std::vector::push_back(value_type&& val) function. Example: std::unique_ptr<int> get_int() { auto p = std::make_unique<int>(1); // `p` is an lvalue but treated as an rvalue in the return statement. , cv1 shall be const), or the reference shall be an rvalue reference. universal reference. And let’s define our storage to be either one of those cases: template<typename T> using Storage = std::variant<Value<T>, ConstReference<T>, NonConstReference<T>>; Now we need to give access to the underlying value of our variant, by providing a reference. Whether it’s heap or stack, and it’s addressable. From the linked documentation. That is any named parameter of a function cannot be implicitly casted or used to initialize another rvalue reference; it only copies to lvalue references; but static_cast can explicitly cast the valueness of the reference. Yes. @eerorika In your example y is an int, so it qualifies for rvalue conversion on return. There is no implicit conversion as suggested in the title, the reference binds directly to the. 4. And an identifier "is an lvalue if the entity is a function or variable" (5. Indeed it does. 3. An obvious example of an lvalue expression is an identifier with suitable type and storage class. So we declare a variable x: int x = 42; An expression x in this scope is now an lvalue (so also a glvalue). Note that there is one exception: there can be lvalue const reference binding to an rvalue. A reference (“lvalue reference” since C++11) is a type of C++ variable that can act as an alias to another value. For the second overload, it would call operator const P&() const&. I recently filed a bug against MSVC which relates to this, where the non-standard behavior caused standard-compliant code to fail to compile and/or compile with a deviant behavior. Using lvalue references where rvalue references are required is an error: int& func2(){//compilation error: cannot bind. . The usual solution is to give the temporary a name, and then pass it like: Now, along comes C++0x - and now with rvalue references, a function defined as void foo (T&&) will allow me to. So. rvalue references are considered lvalue (this part I understand) They are not. e. Thus, both a rvalue and another value can be assigned to values. With string as template argument you get string&& in the function parameter, which is a rvalue reference that doesn't accept lvalues. Overload resolution is used to select the conversion function to be invoked. For example in an expression. Operationally, the difference among these kinds of expressions is this:std::function can be move-constructed from rvalue of a functor object. reinterpret_cast reinterpret_cast converts any pointer type to any other pointer type, even of unrelated classes. 2. But in the circumstances of the linked question, the template instantiation of std::function cannot be inferred from the lambda type. 12. In fact, in terms of overload resolution, an rvalue prefers to be bound to an rvalue reference than to an lvalue const reference. std::move doesn't move anything, it just converts the type of the expression to an rvalue reference. 1Primary categories lvalue prvalue xvalue 2Mixed categories glvalue rvalue 3Special categories Pending member function call Void expressions Bit-fields Move. An lvalue does not necessarily permit modification of the object it designates. Sorted by: 1. Each C++ expression (an operator with its operands, a literal, a variable name, etc. — Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i. What you're referring to is the fact that if an expression. 1, 4. For example second type of the pair should be std::string, not const std::string * and all your problems would go away. You can convert an lvalue to an rvalue by casting it to an xvalue; this is conveniently encapsulated into the type-deducing cast. You can disable this behaviour with the /Za (disable language extensions) compiler switch under. Converts between types using a combination of explicit and implicit conversions. If an lvalue-to-rvalue conversion were performed on s, it would also call the copy constructor; [conv. Radius: 2 2 4. The C++ Standard does use the term rvalue, defining it indirectly with this sentence: "Every expression is either an lvalue or an rvalue. 2. If you really want to pass i to g (), you have two options: provide a temporary object which is a copy of i (then considered as a rvalue) g (int {i}) force the conversion to rvalue reference with std::move (); then the original i must not. An rvalue is a prvalue or an xvalue. An lvalue may get converted to an rvalue: that's something perfectly legit and it happens quite often. Therefore, if we make a reference parameter const, then it will be able to bind to any type of argument:According to the rvalue reference proposal, a named rvalue is no different from an lvalue, except for decltype. In k++, the expression k is an l-value (roughly speaking, it has a name), which is its value-category. This is a changeable storage location. Note that there is one exception: there can be lvalue const reference binding to an rvalue. Template argument deduction deduces T to be X, so the parameter has type X&&. Therefore it makes sense that they are mutable. Correct. Yes, the type of the variable r is indeed int&&. This is its value category. In example 4, a is an lvalue, becuase it has a name and I can take its address so it's ok to bind a lvalue reference b to an lvalue (int&& a) that happens to be a rvalue reference. For fundamental types, the copy approach is reasonable. 1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue or binding an rvalue reference. foo now is null. first) as same as the implementation of std_pair. rvalue references allow automatic moving/copying based upon context (for example the moving of a temporary) trying to simulate this with an lvalue style copy constructor (which actually performed a move) would likely be disastrous. The output is: Copy constructor with lvalue reference. The terms "lvalue/rvalue reference" and "lvalue/rvalue" are related but not interchangeable or one a shortened form of the other. It can convert between pointers. rvalue references are sausage-making devices added later after nobody could find a. It is VC++'s evil extension. First the compiler performs an implicit array-to-pointer conversion for "abc", so the type of "abc" becomes const char*. An lvalue may get converted to an rvalue: that's something perfectly legit and it happens quite often. b is just an alternative name to the memory assigned to the variable a. C++0x: rvalue reference versus non-const lvalue. Lvalues and rvalues are fundamental to C++ expressions. Without lvalue-to-rvalue conversion, it cannot read it's value. Second (and you probably missed that), const char* is converted to a rvalue std::string via the const char* non-explicit constructor of std::string (# 5 in the link). Regarding the second question. There is no lvalue-to-rvalue conversion in this scenario. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression; vector has two overloads of assignment operator, one for Lvalue reference. lvalues. The type and value of the result are the type and value of the right operand; the result is an lvalue if its right operand is. I expect that when using a temporary instance of a Wraper object, the conversion operator defined for rvalue will always be used. Rvalues are the only expression types valid for move operations: std::move and std::forward explicitly attempt to convert arguments to rvalue references. You do not need workaround on how to use rvalue as lvalue, but rather fix your code that you do not need this workaround. I believe this code is both well-formed and well-defined. We provide you with easy how-to’s and step-by-step instructions that provide understanding and guidance for a successful installation process, ensuring professional results. Both lvalue references and rvalue references are a compound type. 6. The Rvalue refers to a value stored at an address in the memory. 25, or 4 (leaving off the units for brevity). Let’s turn it around a bit. This type of static_cast is used to implement move semantics in std::move. Even though the object in question is a temporary object, its lifetime has been extended. – NathanOliver. An lvalue reference (commonly just called a reference since prior to C++11 there was only one type of reference) acts as an alias for an existing lvalue (such as a variable). The lvalue is. 21. That stops the move if it is an lvalue reference. about undefined behaviorIf T is a reference an lvalue-reference type, the result is an lvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (conv. Returning an explicit rvalue-reference. The value of x is 1. (since C++11)20. I would respect the first compiler more, it is at least honest with its inefficiency. int a =5; int b = 3; int c = a+b; the operator + takes two rvalues. The implicitly defined copy constructor takes an lvalue reference (i. This distinction is very important and seems to be overlooked by most when introduced to the topic. 2 Infinite. The idea is that if you have a reference binding that could have been a direct binding if only the reference were of the appropriate kind (i. addv<Adder,int,int>(std::move(adder),a,b); Edit: Convert might be a bit misleading. In the function, the argument has a name and thus is an lvalue. Except for an implicit object parameter, for which see 13. e. An lvalue or xvalue is an expression that refers to such an object. 1/2 (your. A pointer is a type. . Every expression is either an lvalue or an rvalue, so, an rvalue is an expression that does not represent an object occupying. 23. Like this: template <typename T> void foo (T &&value) { f (std::forward<T> (value)); } Here, T &&value is called a forwarding reference (as long T is deduced by the compiler. 2, and 4. 2 Lvalue-to-rvalue conversion [conv. It is used to convert an lvalue into an rvalue. But Args itself is either an lvalue reference or not a reference. The first are categories for the type of a variable/member. For the class type A, f (a); causes the copy constructor of A to be invoked. That works well with normal variables but uint8Vect_t(dataBlock. Once a move constructor is called upon the reference, the original object should be reset to the origin state, and so does any reference to it. However, there is no reason why converting from one reference type to another as a cast should do anything at run time. 1) Is actually not so arbitrary. Select the Configuration Properties > C/C++ > Language property page. 3. An lvalue (locator value) represents an object that occupies some identifiable location in memory (i. key here is Key&& key - this is an lvalue! It has a name, and you can take its address. An rvalue reference is a new type. If you compile with /W4 then the compiler will warn you. An lvalue or xvalue is an expression that refers to such an object. const T& is the O. The addition operator + (and all other binary operators) requires both operands to be rvalue, and the result is rvalue. Lvalue to rvalue conversion. In C++ results of conversions are always rvalues (unless you convert to reference type). Now an lvalue reference is a reference that binds to an lvalue. Visual Studio warning disappears if one removes std::move. rvalues can bind to rvalue references and const lvalue references, e. The rvalue-reference version can't be called with an lvalue argument. Conversion of a function pointer to void * shall not alter the representation. 99 * @return The parameter cast to an rvalue-reference to allow moving it. C++98 the rhs in built-in pointer-to-member access operators could be an lvalue can only be an rvalue CWG 1800: C++98 when applying & to a non-static data member of a member anonymous union, it was unclear whether the anonymous union take a part in the result type the anonymous union is not included in the result type CWG. Otherwise, the type of the rvalue (until C++11) prvalue (since C++11) is T. All standard. Rvalue references are a feature of C++ that was added with the C++11 standard. The address-of operator can only be used on lvalues. const A& ), and lvalue-to-rvalue conversion is suppressed when binding lvalue-reference. 3. static_cast can do other things, as listed in 5. Expressions don't have return types, they have a type and - as it's known in the latest C++ standard - a value category. Rvalue to lvalue conversion? 2. Then std::forward<SomeClass&> (element) will be invoked, and the instantiation of std::forward would be. That means std::move could take both lvalue and rvalue, and convert them to rvalue unconditionally. lvalue. accesses its value), casts that value to T1, constructs a temporary of type T1 (with value 1, since that is the value of b and is a valid value of type T1 ), and binds it to an rvalue. std::forward<T>(p). The following table lists exceptions to this rule. It doesn't need to get the value of. write_Rvalue will only accept an rvalue. "Hello, World" is not of type const char*. As regards the concept, notice that there's no argument-parameter pair on the value level. 1) does not accept such code (makes perfect sense). Informally this conversion is "evaluating" or "taking the value of" the object that the lvalue refers to. Another example of conversion: int c = 6; &c = 4; //ERROR: &c is an rvalue On the contrary you cannot convert an rvalue to an lvalue. I would respect the first compiler more, it is at least. The expression *this is an lvalue; A {} is an rvalue (prvalue) even though they designate the same temporary object. 5. We create two types of access: one const and one not const. It can appear only on the right-hand side of the assignment operator. I could have used std::move to convert the lvalue to rvalue reference and the call would be successful. You can use an lvalue almost anywhere where an rvalue is required and an implicit lvalue to rvalue conversion will occur automatically. xvalue always refers to an expression. Both of g and h are legal and the reference binds directly. Overload resolution is usually done in terms of a strict partial. This is what std::move is for. A conditional expression can be an lvalue or an rvalue. OK. If you write arg+1 inside the function, the lvalue expression arg of type int would undergo this conversion to produce a prvalue expression of type int, since that's what built-in + requires. 3. std::forward<> will make sure to convert the "value category" x to match its type.