C++ conditional operator with void operand(s) - c++

I’m trying to understand the following excerpt from the C++ standard (ISO/IEC 14882:2003, newer versions say essentially the same):
5.16 Conditional operator
2 If either the second or the third operand has type (possibly cv-qualified) void, then the lvalue-to-rvalue
(4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the second
and third operands, ...
I am inclined to thinking that in this context, when an operand is a function call, then the type of the operand is taken to be (although it is not) the function return type. If that is so, then that yields an example of a void type.
I also imagine that a throw expression could also be surmised to have type void in this context, independently of the type of the throw operand. That would then be another example.
Are my two assumptions right? Are there any other cases?
Many thanks

About throw, yes, there is no result, so the type is void and the type of the throw operand is irrelevant. I'm not sure how it could be relevant so that question seems odd.
About functions, I don't know why you say the type of the operand is not the function return type if the operand is a function call. What else would it be? It's where the operand is a function (as opposed to a function call) that function-to-pointer conversion would kick in.
The result type of the conditional operator depends on the type of it's operands.
I think the special language around one of the operands being void is simply that the result of the conditional operator is then void, so the rules about possibly performing conversions of operands of different types are not relevant.

Related

What is "Unary Constructor"?

Here in this link it says:
The static_cast keyword can be used for any normal conversion between
types. Conversions that rely on static (compile-time) type
information. This includes any casts between numeric types, casts of
pointers and references up the hierarchy, conversions with unary
constructor, conversions with conversion operator. For conversions
between numeric types no runtime checks if data fits the new type is
performed. Conversion with unary constructor would be performed even
if it is declared as explicit.
Also here:
To interoperate well with other CLS-compliant languages, you may wish
to wrap each user-defined unary constructor for a given class with a
corresponding convert-from operator.
What do they mean by "Unary Constructor"?
Unary means one, so what they are talking about is a constructor with a single parameter. The standard name for such a thing is a conversion constructor.
Unary refers to one or singular, so a 'Unary constructor' ideally refers to a constructor with a single parameter.

Incompatible operand types when using ternary conditional operator

This code:
bool contains = std::find(indexes.begin(), indexes.end(), i) != indexes.end();
CardAbility* cardAbility = contains ? new CardAbilityBurn(i) : new CardAbilityEmpty;
gives me the following error:
Incompatible operand types CardAbilityBurn and CardAbilityEmpty
However if I write the code like this:
if (contains)
{
cardAbility = new CardAbilityBurn(i);
}
else
{
cardAbility = new CardAbilityEmpty;
}
then the compiler doesn't mind. Why so? I want to use ternary conditional operator because it is just one line. What's wrong there?
I need to note (I think you might need this information) that CardAbilityEmpty and CardAbilityBurn both derive from CardAbility so they are so to say brothers.
Thanks
C++'s type system determines expressions' types from the inside out[1]. That means that the conditional expression's type is determined before the assignment to CardAbility* is made, and the compiler has to choose with only CardAbilityBurn* and CardAbilityEmpty*.
As C++ features multiple inheritance and some more possible conversion paths, since none of the types is a superclass of the other the compilation stops there.
To compile successfully, you need to provide the missing part : cast one or both operands to the base class type, so the conditional expression as a whole can take that type.
auto* cardAbility = contains
? static_cast<CardAbility*>(new CardAbilityBurn(i))
: static_cast<CardAbility*>(new CardAbilityEmpty );
(Note the use of auto, since you already provided the destination type in the right-side expression.)
It is however a bit convoluted, so in the end the if-else structure is better-suited in this case.
[1] There is one exception : overloaded function names have no definitive type until you convert them (implicitly or explicitly) to one of their versions.
There are several cases described for Microsoft compilers, how to handle operand types.
If both operands are of the same type, the result is of that type.
If both operands are of arithmetic or enumeration types, the usual
arithmetic conversions (covered in Arithmetic Conversions) are performed to
convert them to a common type.
If both operands are of pointer types or if one is a pointer type and the
other is a constant expression that evaluates to 0, pointer conversions are
performed to convert them to a common type.
If both operands are of reference types, reference conversions are
performed to convert them to a common type.
If both operands are of type void, the common type is type void.
If both operands are of the same user-defined type, the common type is
that type.
If the operands have different types and at least one of the operands
has user-defined type then the language rules are used to
determine the common type. (See warning below.)
And then there is a caution:
If the types of the second and third operands are not identical, then
complex type conversion rules, as specified in the C++ Standard, are
invoked. These conversions may lead to unexpected behavior including
construction and destruction of temporary objects. For this reason, we
strongly advise you to either (1) avoid using user-defined types as
operands with the conditional operator or (2) if you do use
user-defined types, then explicitly cast each operand to a common
type.
Probably, this is the reason, Apple deactivated this implicit conversion in LLVM.
So, if/else seems to be more appropriate in your case.

Defaul compiler generates the reference operator (In C++)?

All the authors write that, by default, the compiler generates
1. the default constructor
2. copy constructor
3. assignment operator
4. destructor
The other day a friend of mine invited for an interview and there he was told that by default, the compiler generates still one function reference operator (operator &()). it is in fact so?
No, that is not true. If there is no operator& present, then the one in the core language is used.
N3485 13.3.1.2 [over.match.oper]/1-2 (emphasis mine):
If no operand of an operator in an expression has a type that is a class or an enumeration, the operator is assumed to be a built-in operator and interpreted according to Clause 5. [ Note: Because ., .*, and :: cannot be overloaded, these operators are always built-in operators interpreted according to Clause 5. ?: cannot be overloaded, but the rules in this subclause are used to determine the conversions to be applied to the second and third operands when they have class or enumeration type (5.16). —end note ]
If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator.
You can also see this in that the functions implicitly declared for you are listed as the "Special Member Functions (Clause 12), which makes no reference to operator&.

When will default argument promotions happen?

In C language, compiler perform default argument promotion when the function called does not have a prototype.
But how about C++? When will default argument promotions happen?
In C++11 standard 5.2.2/7:
When there is no parameter for a given argument, the argument is
passed in such a way that the receiving function can obtain the value
of the argument by invoking va_arg (18.10). [ Note: This paragraph
does not apply to arguments passed to a function parameter pack.
Function parameter packs are expanded during template instantiation
(14.5.3), thus each such argument has a corresponding parameter when a
function template specialization is actually called. —end note ] The
lvalue-to-rvalue (4.1), array-to-pointer (4.2), and
function-to-pointer (4.3) standard conversions are performed on the
argument expression. An argument that has (possibly cv-qualified) type
std::nullptr_t is converted to type void* (4.10). After these
conversions, if the argument does not have arithmetic, enumeration,
pointer, pointer to member, or class type, the program is ill-formed.
Passing a potentially-evaluated argument of class type (Clause 9)
having a nontrivial copy constructor, a non-trivial move constructor,
or a non-trivial destructor, with no corresponding parameter, is
conditionally-supported with implementation-defined semantics. If the
argument has integral or enumeration type that is subject to the
integral promotions (4.5), or a floating point type that is subject to
the floating point promotion (4.6), the value of the argument is
converted to the promoted type before the call. These promotions are
referred to as the default argument promotions.
This paragraph still does not specify when will a default argument promotion happen. This paragraph may talk too much without a clear logic. I strove to outline the logic but failed. I am not familiar with the invoking va_arg.
Hope you help me.
Default promotions will happen before the function is called, in the calling context.
If you're really asking about the circumstances under which default promotions are carried out, that's covered in the excerpt, though it's such a tiny piece that it's easy to miss: "When there is no parameter for a given argument...". In other words, it's essentially identical to the situation in C, with the exception that a C-style function declaration that doesn't specify parameter types simply doesn't exist in C++. Therefore, the only time you can have an argument without a parameter specifying its type is when a function has an explicit ellipsis, such as printf: int printf(char const *format, ...);.
From the very paragraph you quote in your question: "the value of the argument is converted to the promoted type before the call".
You say of C "default argument promotion when the function called does not have a prototype" - but remember that scenario doesn't exist in C++ - you can not call a function for which no declaration or definition has been seen.
The mention of "invoking va_arg" means that some of the argument promotions are applied when calling a function that will then access the values using the va_arg functions (see http://linux.die.net/man/3/va_arg). Think of it like this: one function call might pass the value int(3), another int(7777), yet another char(7) - how should the called function know what to expect? It will probably promote all values for that parameter to some largest-supported-integral type such as an int or long, then when va_arg is used within the function it will convert from int or long to whatever integral type the va_arg call specifies. This does mean, for example, that int(7777) value might be passed where only a char is expected and the value may be truncated to 8 bits without warning, but that's generally better than having the program crash because the number of bytes of data passed didn't match the number consumed, or some other weird side effect.

Can anyone explain this paragraph of the current C++0x standard draft?

Can anyone explain this statement from ISO N3242 §3.2, 2nd point
An expression is potentially evaluated unless it is an unevaluated operand
(Clause 5) or a subexpression thereof. A variable or non-overloaded
function whose name appears as a potentially-evaluated expression is
odr-used unless it is an object that satisfies the requirements for appearing in a
constant
expression (5.19) and the lvalue-to-rvalue conversion (4.1) is
immediately
applied. this is odr-used if it appears as a potentiallyevaluated
expression
(including as the result of the implicit transformation in the body of
a
non-static member function (9.3.1)).
ISO Standard 2003 : says
An expression is potentially evaluated unless it appears where an
integral
constant expression is required (see 5.19), is the operand of the
sizeof
operator (5.3.3), or is the operand of the typeid operator and the
expression
does not designate an lvalue of polymorphic class type (5.2.8). An
object or
non-overloaded function is used if its name appears in a
potentially-evaluated
expression.
What is the actual difference in these statements?
Can any one explain this with the help of an example/program?
"unevaluated operand" replaces "is the operand of the sizeof operator (5.3.3), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8)". It has the same basic purpose, but doesn't try to list all the cases in the C++0x standard of operators whose operands aren't evaluated. decltype is a new one, for example.
"odr-used" replaces "used", I presume they figured that "used" alone might be ambiguous with other uses of the word "use" in the standard. In both cases, though, it's defining the sense of "used" which is relevant to the ODR.
So those aren't really changes, just re-wordings updated for C++0x.
This is a change:
A variable or non-overloaded function
whose name appears as a
potentially-evaluated expression is
odr-used unless it is an object
that satisfies the requirements for
appearing in a constant expression
(5.19) and the lvalue-to-rvalue
conversion (4.1) is immediately
applied.
vs.
An object or non-overloaded
function is used if its name appears
in a potentially-evaluated
expression.
Suppose a is a static const int at global scope. Then in C++03 it is not used in the following statement:
char x[a];
because the context requires a constant expression. However, it is used in the following:
void foo(int); foo(a);
because the context doesn't require a constant expression.
In C++0x, a is not odr-used in either case. It's allowed to be in a constant expression, and in the function call, lvalue-rvalue conversion is immediately applied (because foo takes its parameter by value, not reference). So it qualifies for the "unless" which wasn't present in C++03.
There's also a difference in the definition of "potentially evaluated". In the first example, char x[a], a is potentially evaluated in C++03 but not in C++0x. I haven't checked whether anything else in the standard uses "potentially evaluated", that might be affected by this change. If it's only mentioned here then that part of it isn't a change, it's just that the exception has been moved from "potentially evaluated" to "used".