What is decltype(0 + 0)?
Asked Answered
B

5

14

(Prompted by an answer.)

Given N3290, §7.1.6.2p4, where the list items are unnumbered, but numbered here for our convenience:

The type denoted by decltype(e) is defined as follows:

  1. if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
  2. otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
  3. otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
  4. otherwise, decltype(e) is the type of e.

What is the type specified by decltype(0 + 0)?

Item 1 doesn't apply, 2 might, but if not, then 3 doesn't apply and 4 would be the result. So, what is an xvalue, and is 0 + 0 an xvalue?

§3.10p1:

An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2).

I don't see anything in §8.3.2 that would be helpful here, but I do know "0 + 0" doesn't involve any rvalue-references. The literal 0 is a prvalue, which is "an rvalue that is not an xvalue" (§3.10p1). I believe "0 + 0" is also a prvalue. If that's true, "decltype(0 + 0)" would be int (not int&&).

Have I missed something in my interpretation? Is this code well-formed?

decltype(0 + 0) x;  // Not initialized.

The code compiles on GCC 4.7.0 20110427 and Clang 2.9 (trunk 126116). It would not be well-formed if the decltype specified an int&& type, for example.

Behindhand answered 8/5, 2011 at 0:21 Comment(6)
I don't see anything wrong with your reasoning. I believe that decltype(0 + 0) should be int, too.Leprechaun
FWIW, a good answer in the affirmative would be to expand on the definition of xvalue and show what it can and cannot be. (I would find that very helpful.) I need to see how to reword to focus on "what is an xvalue?" while still considering this concrete case of "0 + 0".Behindhand
Although the latest draft does specify the value category for many expressions, including things like postfix increment and a note in 3.10 indicates that clause 5 should show the category of the value for each built-in operator, the draft doesn't seem to mention a value category for any of the binary operators from 5.6 to 5.15 unless my search powers have failed me.Leprechaun
Disregarding of what the spec says, the intent is that 0 + 0 is a prvalue. lvalue = identity and not movable. xvalue = identity and movable. prvalue = no identity and movable. An xvalue is an expression that refers to an object (and objects in C++ have an unique identity, determined by address, type and lifetime), and that object may be moved from (is considered eXpiring). This is my silly explanation, of course not to be found in the spec.Religiose
@JohannesSchaub-litb: Rvalues can be xvalues; when do rvalues have identity? (I'm sure it's something simple I'm overlooking, but I'm curious and can't see it.)Behindhand
@Fred when rvalues are xvalues, they have identity. Example: int a; (int&&)a; the xvalue the cast yields refers to an object. Another, (int&&)2;, the temporary bound by the reference has identity. Its lifetime will end at the end of the full expression. A (non-class, non-array) prvalue has no identity. Example 2, which is no different from another 2 appearing in the code, or from 1+1, etc..Religiose
P
2

From 5.19 [expr.const], every literal constant expression is a prvalue.

A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An integral constant expression is a literal constant expression of integral or unscoped enumeration type.

Therefore rule 4 applies to all literal constant expressions.

Predilection answered 30/5, 2011 at 19:20 Comment(7)
Thanks, though I still see a hole in the semantics (as pointed out by Charles in a comment on the question) for "int n = 42; decltype(0 + n)". :(Behindhand
@Fred: And of course, although the standard makes it clear that 0 + 0 is a core constant expression, its prvalue status remains in doubt. Obviously if it weren't a prvalue it couldn't be an integral constant expression and a LOT of things would break.Predilection
You have this backwards: that paragraph is defining the term literal constant expression.Spiculum
@Richard: That paragraph is indeed a definition. A definition provides necessary conditions, and can be used to argue about the properties of said items. There's nothing backwards.Predilection
@BenVoigt You appear to be saying that 0 + 0 is a prvalue because it is a literal constant expression. However, in order to determine that 0 + 0 is a literal constant expression, we must first determine whether it is a prvalue, which is begging the question. Whether an expression is a constant expression does not affect its value category; a useful answer would refer to 3.10 or 5/6, which explain which expressions are xvalues.Spiculum
@RichardSmith: That's what I said in my comment above, isn't it?Predilection
-2/2: agreed with @Richard. you have not shown that 0 + 0 is a prvalue. while your reply is not wrong in itself, it appears to be insignificant to the questionReligiose
S
10

0 + 0 is an expression of two prvalues, (n3290 par. 3.10) which applies the built-in operator+, which, per 13.6/12 is LR operator+(L,R), which is therefore a function that returns something that is not a reference. The result of the expression is therefore also a prvalue (as per 3.10).

Hence, the result of 0 + 0 is a prvalue, 0 is an int, therefore the result of 0 + 0 is an int

Shovelboard answered 8/5, 2011 at 2:55 Comment(6)
§13.6p9 is unary operator+. You want §13.6p12. However, the draft says "These candidate functions participate in the operator overload resolution process as described in 13.3.1.2 and are used for no other purpose." (§13.6p1) I believe "no other purpose" means we can't use them to determine value category. +1 for a good answer anyway.Behindhand
the built-in operators are not function calls. So you cannot take 13.6/12 and get the described return type of those candidates and apply them back to clause 5. Those candidates of 13.6 are only active and relevant when you do overload resolution (if at least one operand is of class or enumeration type). And they are only used for converting class type operands. After that is done, control is completely given back to clause 5. That "LR operator+" is not an actual function that is somehow called.Religiose
@JohannesSchaub-litb I agree, but it's the closest thing in the (draft) standard to an answer to the question. Fact is that, as @Fred mentioned in chat, clause 3.10 promises that clause 5 will provide the semantics, and clause 5 breaks that promise. The closest thing we then have to something to plug the semantic hole is to consider the built-in operators as function calls, consider their prototypes in clause 13 as their definitions (despite the fact that the draft says that those candidate functions are used for no other purpose than overload resolution). and reason on from there.Shovelboard
@JohannesSchaub-litb: More importantly for this discussion, the "used for no other purpose" notice explicitly disallows using §13.6p12 for the purpose of answering this question.Behindhand
But... are return type and value category not always determined by overload resolution?Predilection
@BenVoigt maybe, but that doesn't mean that the result of a built-in operator can be construed as a return value from a function call - which maintains the hole in my reasoning (and the semantic hole in the standard)...Shovelboard
L
5

It is definitely an int:

#include <iostream>
#include <typeinfo>

template<typename T>
struct ref_depth
{
        enum { value = 0 };
};

template<typename T>
struct ref_depth<T&>
{
        enum { value = 1 };
};

template<typename T>
struct ref_depth<T&&>
{
        enum { value = 2 };
};

int main() {

  std::cout
    << "int: " << typeid(int).name() << "\n"
       "decltype(0 + 0): " << typeid(decltype(0 + 0)).name() << "\n"
       "int&&: " << typeid(int&&).name() << "\n";
  std::cout 
    << "ref_depth: int: " << ref_depth<int>::value << "\n"
       "ref_depth: decltype(0 + 0): " << ref_depth<decltype(0 + 0)>::value << "\n"
       "ref_depth: int&&: " << ref_depth<int&&>::value << "\n";

}

Output:

int: i
decltype(0 + 0): i
int&&: i
ref_depth: int: 0
ref_depth: decltype(0 + 0): 0
ref_depth: int&&: 2
Localism answered 8/5, 2011 at 3:13 Comment(2)
Could you please include the code in the answer? (Rather than summarizing the code's conclusion in the answer and linking to a completely different site.)Behindhand
@Fred Nurk: I've taken the liberty.Sinless
B
2

Your reasoning is correct. An expression involving only constants is a constant by itself. Thus

decltype(0 + 0) x;

equals

decltype(0) x;

which equals

int x;
Brachycephalic answered 8/5, 2011 at 1:2 Comment(1)
This doesn't answer the question. Using zeros is a placeholder, and a good answer will show how to extrapolate to decltype(some_int + another_int) (or show why zero literals are special).Behindhand
P
2

From 5.19 [expr.const], every literal constant expression is a prvalue.

A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An integral constant expression is a literal constant expression of integral or unscoped enumeration type.

Therefore rule 4 applies to all literal constant expressions.

Predilection answered 30/5, 2011 at 19:20 Comment(7)
Thanks, though I still see a hole in the semantics (as pointed out by Charles in a comment on the question) for "int n = 42; decltype(0 + n)". :(Behindhand
@Fred: And of course, although the standard makes it clear that 0 + 0 is a core constant expression, its prvalue status remains in doubt. Obviously if it weren't a prvalue it couldn't be an integral constant expression and a LOT of things would break.Predilection
You have this backwards: that paragraph is defining the term literal constant expression.Spiculum
@Richard: That paragraph is indeed a definition. A definition provides necessary conditions, and can be used to argue about the properties of said items. There's nothing backwards.Predilection
@BenVoigt You appear to be saying that 0 + 0 is a prvalue because it is a literal constant expression. However, in order to determine that 0 + 0 is a literal constant expression, we must first determine whether it is a prvalue, which is begging the question. Whether an expression is a constant expression does not affect its value category; a useful answer would refer to 3.10 or 5/6, which explain which expressions are xvalues.Spiculum
@RichardSmith: That's what I said in my comment above, isn't it?Predilection
-2/2: agreed with @Richard. you have not shown that 0 + 0 is a prvalue. while your reply is not wrong in itself, it appears to be insignificant to the questionReligiose
K
0

GCC says int-

Code:

#include <iostream>
#include <typeinfo>

int
main ()
{
  int n;
  decltype(0 + 0) x;
  std::cout << "Type of `n': " << typeid(n).name() << std::endl;
  std::cout << "Type of `x': " << typeid(x).name() << std::endl;
}

Output:

i

i

Edit: It makes sense according to point 4, but I can't say for sure that point 2 isn't actually the one in effect. From what I can tell, 0 + 0 is evaluated to 0, and the type of 0 is int, so that is the declared type.

Kopans answered 8/5, 2011 at 0:48 Comment(1)
That is true. Note that with GCC, int&& is the same as int. After all, a reference to an int IS an int. After all, a reference is only an alias in theory. Try if (typeid(int) == typeid(int&&)) { std::cout << "int == int&&" << std::endl; }. Even if the strings for them are the same, the actual typeid wouldn't be the same unless they're treated EXACTLY the same. This makes me wonder what's going on underneath it all... For now, I'd say that decltype(0 + 0) results in type int.Kopans

© 2022 - 2024 — McMap. All rights reserved.