#include <string>
struct X
{
char y;
std::string z;
X & operator()(std::string && s)
{
z = std::move(s);
return *this;
}
X & operator()(char c)
{
y = c;
return *this;
}
};
int main()
{
X x;
std::string y("abc");
x(y[0])(std::move(y));
}
is the last line of main undefined behavior? I'm guessing yes because it would unfold to something like the following but just want to make sure that there are no stricter guarantees on call operators or member function invocations in general
X::operator()(&X::operator()(&x, y[0]), std::move(z))
Please add references from the standard or cppref
Before c++17, chaining function calls that modify the same l-value, like in your example, is indeed undefined behavior, since the order of evaluation of these expressions is unspecified.
However, a proposal to fix that was merged into c++17.
Here's the relevant rule (emphasis mine), which also contains an example from the proposal that shows how this works:
The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter. [Note: All side effects of argument evaluations are sequenced before the function is entered (see [intro.execution]). — end note] [Example:
void f() {
std::string s = "but I have heard it works even if you don't believe in it";
s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, "");
assert(s == "I have heard it works only if you believe in it"); // OK
}
— end example]
While the above rule only strictly refers to the built-in operator(), and you have user-defined operators, the same rules about order of evaluation apply, because of this rule:
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. ... However, the operands are sequenced in the order prescribed for the built-in operator.
Related
I'm pretty new to C++ and recently I ran across some info on what it means for a variable to be volatile. As far as I understood, it means a read or write to the variable can never be optimized out of existence.
However a weird situation arises when I declare a volatile variable that isn't 1, 2, 4, 8 bytes large: the compiler(gnu with C++11 enabled) seemingly ignores the volatile specifier
#define expand1 a, a, a, a, a, a, a, a, a, a
#define expand2 // ten expand1 here, expand3 to expand5 follows
// expand5 is the equivalent of 1e+005 a, a, ....
struct threeBytes { char x, y, z; };
struct fourBytes { char w, x, y, z; };
int main()
{
// requires ~1.5sec
foo<int>();
// doesn't take time
foo<threeBytes>();
// requires ~1.5sec
foo<fourBytes>();
}
template<typename T>
void foo()
{
volatile T a;
// With my setup, the loop does take time and isn't optimized out
clock_t start = clock();
for(int i = 0; i < 100000; i++);
clock_t end = clock();
int interval = end - start;
start = clock();
for(int i = 0; i < 100000; i++) expand5;
end = clock();
cout << end - start - interval << endl;
}
Their timings are
foo<int>(): ~1.5s
foo<threeBytes>(): 0
I've tested it with different variables (user-defined or not) that is 1 to 8 bytes and only 1, 2, 4, 8 takes time to run. Is this a bug only existing with my setup or is volatile a request to the compiler and not something absolute?
PS the four byte versions always take half the time as others and is also a source of confusion
The struct version will be optimized out probably, as the compiler realizes that there's no side effects (no read or write into the variable a), regardless of the volatile. You basically have a no-op, a;, so the compiler can do whatever it pleases it; it is not forced to unroll the loop or to optimize it out, so the volatile doesn't really matter here. In the case of ints, there seems to be no optimizations, but this is consistent with the use case of volatile: you should expect non-optimizations only when you have a possible "access to an object" (i.e. read or write) in the loop. However what constitutes "access to an object" is implementation-defined (although most of the time it follows common-sense), see EDIT 3 at the bottom.
Toy example here:
#include <iostream>
#include <chrono>
int main()
{
volatile int a = 0;
const std::size_t N = 100000000;
// side effects, never optimized
auto start = std::chrono::steady_clock::now();
for (std::size_t i = 0 ; i < N; ++i)
++a; // side effect (write)
auto end = std::chrono::steady_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< " ms" << std::endl;
// no side effects, may or may not be optimized out
start = std::chrono::steady_clock::now();
for (std::size_t i = 0 ; i < N; ++i)
a; // no side effect, this is a no-op
end = std::chrono::steady_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< " ms" << std::endl;
}
EDIT
The no-op is not actually optimized out for scalar types, as you can see in this minimal example. For struct's though, it is optimized out. In the example I linked, clang doesn't optimize the code with no optimization, but optimizes both loops with -O3. gcc doesn't optimize out the loops either with no optimizations, but optimizes only the first loop with optimizations on.
EDIT 2
clang spits out an warning: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]. So my initial guess was correct, the compiler can optimize out no-ops, but it is not forced. Why does it do it for structs and not scalar types is something that I don't understand, but it is the compiler's choice, and it is standard compliant. For some reason it gives this warning only when the no-op is a struct, and doesn't give the warning when it's a scalar type.
Also note that you don't have a "read/write", you only have a no-op, so you shouldn't expect anything from volatile.
EDIT 3
From the golden book (C++ standard)
7.1.6.1/8 The cv-qualifiers [dcl.type.cv]
What constitutes an access to an object that has volatile-qualified
type is implementation-defined. ...
So it is up to the compiler to decide when to optimize out the loops. In most cases, it follows the common sense: when reading or writing into the object.
This question is a lot more interesting than it first appears (for some definition of "interesting"). It looks like you've found a compiler bug (or intentional nonconformance), but it isn't quite the one you are expecting.
According to the standard, one of your foo calls has undefined behavior, and the other two are ill-formed. I'll first explain what should happen; the relevant standard quotes can be found after the break. For our purposes, we can just analyze the simple expression statement a, a, a; given volatile T a;.
a, a, a in this expression statement is a discarded-value expression ([stmt.expr]/p1). The type of the expression a, a, a is the type of the right operand, which is the id-expression a, or volatile T; since a is an lvalue, so is the expression a, a, a ([expr.comma]/p1). Thus, this expression is an lvalue of a volatile-qualified type, and it is a "comma expression where the right operand is one of these expressions" - in particular, an id-expression - and therefore [expr]/p11 requires the lvalue-to-rvalue conversion be applied to the expression a, a, a. Similarly, inside a, a, a, the left expression a, a is also a discarded-value expression, and inside this expression the left expression a is also a discarded-value expression; similar logic shows that [expr]/p11 requires the lvalue-to-rvalue conversion be applied to both the result of the expression a, a and the result of the expression a (the leftmost one).
If T is a class type (either threeBytes or fourBytes), applying the lvalue-to-rvalue conversion entails creating a temporary by copy-initialization from the volatile lvalue a ([conv.lval]/p2). However, the implicitly declared copy constructor always takes its argument by a non-volatile reference ([class.copy]/p8); such a reference cannot bind to a volatile object. Therefore, the program is ill-formed.
If T is int, then applying the lvalue-to-rvalue conversion yields the value contained in a. However, in your code, a is never initialized; this evaluation therefore produces an indeterminate value, and per [dcl.init]/p12, results in undefined behavior.
Standard quotes follows. All are from C++14:
[expr]/p11:
In some contexts, an expression only appears for its side effects.
Such an expression is called a discarded-value expression. The
expression is evaluated and its value is discarded. The
array-to-pointer (4.2) and function-to- pointer (4.3) standard
conversions are not applied. The lvalue-to-rvalue conversion (4.1) is
applied if and only if the expression is a glvalue of
volatile-qualified type and it is one of the following:
( expression ), where expression is one of these expressions,
id-expression (5.1.1),
[several inapplicable bullets omitted], or
comma expression (5.18) where the right operand is one of these expressions.
[ Note: Using an overloaded operator causes a function call; the
above covers only operators with built-in meaning. If the lvalue is of
class type, it must have a volatile copy constructor to initialize the
temporary that is the result of the lvalue-to-rvalue conversion. —end
note ]
[expr.comma]/p1:
A pair of expressions separated by a comma is evaluated left-to-right;
the left expression is a discarded-value expression (Clause 5) [...] The type
and value of the result are the type and value of the right operand;
the result is of the same value category as its right operand [...].
[stmt.expr]/p1:
Expression statements have the form
expression-statement:
expression_opt;
The expression is a discarded-value expression (Clause 5).
[conv.lval]/p1-2:
1 A glvalue (3.10) of a non-function, non-array type T can be
converted to a prvalue. If T is an incomplete type, a program that
necessitates this conversion is ill-formed. If T is a non-class
type, the type of the prvalue is the cv-unqualified version of T.
Otherwise, the type of the prvalue is T.
2 [some special rules not relevant here] In all other cases, the
result of the conversion is determined according to the following
rules:
[inapplicable bullet omitted]
Otherwise, if T has a class type, the conversion copy-initializes a temporary of type T from the glvalue and the result of the
conversion is a prvalue for the temporary.
[inapplicable bullet omitted]
Otherwise, the value contained in the object indicated by the glvalue is the prvalue result.
[dcl.init]/p12:
If no initializer is specified for an object, the object is
default-initialized. When storage for an object with automatic or
dynamic storage duration is obtained, the object has an indeterminate
value, and if no initialization is performed for the object, that
object retains an indeterminate value until that value is replaced
(5.17). [...] If an indeterminate value is produced by an evaluation,
the behavior is undefined except in the following cases: [certain
inapplicable exceptions related to unsigned narrow character types]
[class.copy]/p8:
The implicitly-declared copy constructor for a class X will have the
form
X::X(const X&)
if each potentially constructed subobject of a class type M (or
array thereof) has a copy constructor whose first parameter is of type
const M& or const volatile M&. Otherwise, the implicitly-declared
copy constructor will have the form
X::X(X&)
volatile doesn't do what you think it does.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html
If you're relying on volatile outside of the three very specific uses Boehm mentions on the page I linked, you're going to get unexpected results.
See this example:
struct Foo
{
int a;
int b;
bool operator == (const Foo & x)
{
return a == x.a && b == x.b;
}
};
int main ()
{
Foo a;
a = {1, 2};
if (a == {1, 2}) // error: expected primary-expression before ‘{’ token
{
}
}
The line a={1,2} is fine. The braces are convert to a Foo to match the argument type of the implicit operator= method. It still works if operator= is user-defined.
The line if (a=={1,2}}) errors as indicated.
Why does the expression {1,2} not convert to a Foo to match the user-defined operator== method?
List-initialization cannot be used as an argument to an operator in the general case. Per Paragraph 8.5.4/1 of the C++11 Standard:
[...] List-initialization can be used
— as the initializer in a variable definition (8.5)
— as the initializer in a new expression (5.3.4)
— in a return statement (6.6.3)
— as a for-range-initializer (6.5)
— as a function argument (5.2.2)
— as a subscript (5.2.1)
— as an argument to a constructor invocation (8.5, 5.2.3)
— as an initializer for a non-static data member (9.2)
— in a mem-initializer (12.6.2)
— on the right-hand side of an assignment (5.17)
The last item explains why list-initialization is allowed on the right side of operator =, even though it is not allowed in general for an arbitrary operator.
Because of the fifth item above, however, it can be used as an argument to a regular function call, this way:
if (a.operator == ({1, 2}))
It's just simply not supported.
Initializer lists are explicitly defined to be valid in initializations ([C++11: 8.5.4]), and assignments:
[C++11: 5.17/9]: A braced-init-list may appear on the right-hand side of
an assignment to a scalar, in which case the initializer list shall have at most a single element. The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T(v) except that no narrowing conversion (8.5.4) is allowed. The meaning of x={} is x=T().
an assignment defined by a user-defined assignment operator, in which case the initializer list is passed as the argument to the operator function.
There is no standard wording to allow other, arbitrary cases.
If it were allowed, in this example, the type of {1,2} would be fairly ambiguous. It would be a complicated language feature to implement.
An explicit cast is required.
if (a == (Foo){1, 2})
{
}
When you are using the expression containing the user defined type a and == operator
Overloaded operator function gets called which as per definition you have given requires an argument of a reference of an object of class Foo
so your expression should be like a==b
where b is an object of class Foo
Here with this expression you will be able to compare data members of b and a and hence know if they are equal or not
No real reason.
C++ is the result of a committee effort so sometimes strange but deliberate decisions may slip through because of complex political/sociological dynamics.
C++ syntax is hard. Very hard. Almost unbelievably hard. There are rules even go like "if you can parse this arbitrarily long sequence of tokens as both this or that, then it's this".
It took many years for compilers even to simply agree on what is C++ and what is not.
In this case my wild guess is that they didn't like the idea that cases that looked very similar:
MyClass x = {...};
MyClass y; y = {...};
would be handled differently so there is a special provision for assignment to allow the syntax.
From a technical point of view I don't see what are the problems of allowing it for other operators too, and on the other hand if there are problems (e.g. for overloading, template instantiation etc.) I don't see how assignment can hope to escape them.
EDIT
g++ allows using not only strict operator= but also operator+=, operator-= and similar "augmented assignment". May be the logical problems happens only if you allow non-member overloads (that are forbidden for assignment and augmented assignment operators).
Functions marked constexpr are supposed to be immutable pure functions. From the "std::max() and std::min() not constexpr" post, you can't re-channel a const-reference input as an output, since that would require the parameter to have permanence. But can you take a parameter by const-reference, as long as you don't re-channel it?
// Is this still constexpr?
// (Assuming that std::complex is constexpr-safe, as it's supposed to be.)
constexpr
int MySum( std::complex<double> const &a, std::complex<double> const &b )
{ return static_cast<int>( a.real() + b.real() ); }
Conversely, can you return a const-reference to a sub-object of a constexpr-enabled type?
template <typename T>
class MyComplex
{
T c_[ 2 ];
public:
constexpr MyComplex( T r = T(), T i = T() )
: c_{ r, i }
{}
// Is this actually constexpr?
constexpr T const & operator[]( unsigned l ) //const
{ return c_[ l ]; }
// Can't be constexpr
T & operator[]( unsigned l ) { return c_[ l ]; }
};
Or do even sub-object returns have to be by value?
(Sorry if this is basic, but everything I've found dances around this point without actually being definitive.)
The standard is pretty clear on what is allowed in a constexpr function:
§7.1.5 [dcl.constexpr] p3
The definition of a constexpr function shall satisfy the following constraints:
[...]
its return type shall be a literal type;
each of its parameter types shall be a literal type;
[...]
§3.9 [basic.types] p10
A type is a literal type if it is:
a scalar type; or
a reference type; or
a class type (Clause 9) that has all of the following properties:
it has a trivial destructor,
every constructor call and full-expression in the brace-or-equal-initializers for non-static data
members (if any) is a constant expression (5.19),
it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template
that is not a copy or move constructor, and
it has all non-static data members and base classes of literal types; or
an array of literal type.
As such, yes, you can have reference parameters, even reference-to-non-const ones. The parameters of a constexpr function are restricted in another way. The complete, exhaustive list can be found under §5.19 [expr.const] p2. Here's an excerpt of what makes a constexpr declared function not-so-constexpr anymore:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function. —end note ]:
(The last bit about the logical operators just means that the unevaluated part of it (due to short-circuit evaluation) is not part of the operations that determine whether the function is truly constexpr.)
[...]
a dynamic cast (5.2.7);
a reinterpret_cast (5.2.10);
a pseudo-destructor call (5.2.4);
increment or decrement operations (5.2.6, 5.3.2);
a typeid expression (5.2.8) whose operand is of a polymorphic class type;
a new-expression (5.3.4);
a delete-expression (5.3.5);
a subtraction (5.7) where both operands are pointers;
a relational (5.9) or equality (5.10) operator where the result is unspecified;
an assignment or a compound assignment (5.17); or
[...]
Core issue 1454's resolution changes the rule which Johannes is referencing in his answer to the std::max question. With the current resolution of that issue (which is implemented by both g++ and clang), constexpr functions are allowed to return, by reference, any lvalue which they can compute.
I suspect the following chaining of functions would result in unspecified sequence according to the C++ standards (assume C++0x). Just want a confirmation and if anyone could provide an explanation, I'd appreciate it.
#include <iostream>
struct TFoo
{
TFoo(int)
{
std::cout<<"TFoo"<<std::endl;
};
TFoo foobar1(int)
{
std::cout<<"foobar1"<<std::endl;
return *this;
};
TFoo foobar2(int)
{
std::cout<<"foobar2"<<std::endl;
return *this;
};
static int bar1()
{
std::cout<<"bar1"<<std::endl;
return 0;
};
static int bar2()
{
std::cout<<"bar2"<<std::endl;
return 0;
};
static int bar3()
{
std::cout<<"bar3"<<std::endl;
return 0;
}
};
int main(int argc, char *argv[])
{
// is the sequence well defined for bar1, bar2 and bar3?
TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3());
}
* edit: removed __fastcall specifier for functions (not required/relevant to the question).
The evaluation order is not specified. The relevant section of the draft C++0x spec is 1.9, paragraphs 14 and 15:
14 Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
Here the relevant full-expression is:
TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3());
And so the evaluation of its subexpressions are unsequenced (unless there is an exception noted somewhere that I missed).
I am pretty sure earlier standards include language having the same effect but in terms of "sequence points".
[edit]
Paragraph 15 also says:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [Note: Value computations and side effects associated with different argument expressions are unsequenced.— end note]
A "postfix expression designating the called function" is something like the foo().bar in foo().bar().
The "note" here merely clarifies that argument evaluation order is not an exception to the "unspecified order" default. By inference, neither is the evaluation order associated with the "postfix expression designating the called function"; or if you prefer, the evaluation order of the expression for the this argument. (If there were an exception, this would be the natural place to specify it. Or possibly section 5.2.2 that talks about function calls. Neither section says anything about the evaluation order for this example, so it is unspecified.)
Yes, the order of evaluation of function arguments is unspecified.
For me, gcc 4.5.2 on linux produces
bar3
bar2
bar1
TFoo
foobar1
foobar2
but clang++ on linux and gcc 3.4.6 on solaris produce
bar1
TFoo
bar2
foobar1
bar3
foobar2
To analyze a simpler example, TFoo(0).foobar1(TFoo::bar2()); is a call to TFoo::foobar1 which takes two arguments: the result of the subexpression TFoo(0) (as the hidden argument this) and the result of the subexpression Tfoo::bar2(). For me, gcc executs bar2() first, then TFoo's constructor, and then calls foobar1(), while clang++ for example, executes TFoo's constructor first, then bar2() and then calls foobar1().
I picked this up in one of my brief forays to reddit:
http://www.smallshire.org.uk/sufficientlysmall/2009/07/31/in-c-throw-is-an-expression/
Basically, the author points out that in C++:
throw "error"
is an expression. This is actually fairly clearly spelt out in the C++ Standard, both in the main text and the grammar. However, what is not clear (to me at least) is what is the type of the expression? I guessed "void", but a bit of experimenting with g++ 4.4.0 and Comeau yielded this code:
void f() {
}
struct S {};
int main() {
int x = 1;
const char * p1 = x == 1 ? "foo" : throw S(); // 1
const char * p2 = x == 1 ? "foo" : f(); // 2
}
The compilers had no problem with //1 but barfed on //2 because the the types in the conditional operator are different. So the type of a throw expression does not seem to be void.
So what is it?
If you answer, please back up your statements with quotes from the Standard.
This turned out not to be so much about the type of a throw expression as how the conditional operator deals with throw expressions - something I certainly didn't
know about before today. Thanks to all who replied, but particularly to David Thornley.
According to the standard, 5.16 paragraph 2 first point, "The second or the third operand (but not both) is a throw-expression (15.1); the result is of the type of the other and is an rvalue." Therefore, the conditional operator doesn't care what type a throw-expression is, but will just use the other type.
In fact, 15.1, paragraph 1 says explicitly "A throw-expression is of type void."
"A throw-expression is of type void"
ISO14882 Section 15
From [expr.cond.2] (conditional operator ?:):
If either the second or the third operand has type (possibly cv-qualified) void, then the lvalue-to-rvalue,
array-to-pointer, and function-to-pointer standard conversions are performed on the second and
third operands, and one of the following shall hold:
— The second or the third operand (but not both) is a throw-expression;
the result is of the type of the other and is an rvalue.
— Both the second and the third operands have type void;
the result is of type void and is an rvalue.
[ Note: this includes the case where both operands are throw-expressions. — end note ]
So, with //1 you were in the first case, with //2, you were violating "one of the following shall hold", since none of them do, in that case.
You can have a type printer spit it out for you :
template<typename T>
struct PrintType;
int main()
{
PrintType<decltype(throw "error")> a;
}
Basically the lack of implementation for PrintType will cause the compilation error report to say :
implicit instantiation of undefined template PrintType<void>
so we can actually verify that throw expressions are of type void (and yes, the Standard quotes mentioned in other answers verify that this isn't an implementation specific outcome - though gcc has a hard time printing valuable info)