struct my
{
my(){ std::cout<<"Default";}
my(const my& m){ std::cout<<"Copy";}
~my(){ std::cout<<"Destructor";}
};
int main()
{
my m(); //1
my n(my()); //2
}
Expected output :
1 ) Default
2 ) Copy
Actual output :
What's wrong with my understanding of the constructor invoking mechanism?
Note I have omitted header files for brevity.
Case 1)
m is interpreted as a function return my and taking no arguments.
To see the expected output remove () i.e use my m;
Case 2)
This is something better known as the "Most vexing parse".
n is interpreted as a function returning my that takes an argument of type pointer to function returning my taking no arguments.
To see the expected output in this case try my n((my())); [Instead of treating as an argument specification as in the former case the compiler would now interpret it as an expression because of the extra ()]
My interpretation:
my n((my())) is equivalent to my n = my(). Now the rvalue expression my() creates a temporary[i.e a call to the default constructor] and n is copy initialized to that temporary object[no call to the copy-ctor because of some compiler optimization]
P.S: I am not 100% sure about the last part of my answer. Correct me if I am wrong.
Like Prasoon, I suspect the C++ compiler is parsing your code in a way you don't expect. For example, I think it is parsing the line
my m();
as a function prototype declaration, not as a variable declaration and call to the constructor - hence why you see no output.
Related
Here is an example of a simple function style casting done by int(a):
float a = 5.0;
int b = int(a);
More info from cpprefrenece:
The functional cast expression consists of a simple type specifier or a typedef specifier followed by a single expression in parentheses.
I have 2 questions:
1) would using the new operator still count as a functional cast?
int* b = new int(a);
2) Assuming test is a class, then test t = test(1); is a function style casting to test but test t = test(1, 2); isn't because it has more than 1 expression in parenthesis?
would using the new operator still count as a functional cast?
No, the use of the new operator(like you used in your example) is not a use case of functional cast.
test t = test(1, 2); isn't because it has more than 1 expression in parenthesis?
Both test t = test(1); and test t = test(1,2); are copy initializations. Now, the subexpression test(1) is indeed a functional cast where the appropriate test constructor will be used. While the subexpression test(1, 2) is not as it has more than a single expression inside parenthesis.
1) New expression
A new-expression is a special case of... a new-expression. Nothing less, nothing more. It results in creation of object of given type in dynamic storage and , for class-types, in a call to constructor with given argument list. For trivial types new-expression provides an initializer.
A new-expression isn't a cast, albeit initializing parameters from argument list given in parenthesis may involve implicit casts. If returned result of new-expression wasn't used, the created object would continue to exist. And unless there is a curious contingency involved, it would not be correctly freed.
A functional cast would produce an xvalue, an object which would expire at end of enclosing expression.
C++ parser by design is a "greedy" parser. It doesn't try to single out every token first, it's a complex algorithm to match statements to whole patterns, as longer as possible, appropriate for current context (and context may change in result). The "greedy" nature becomes obvious in case of some ill-formed or ambiguous code. As a rule, if something can be parsed wrong, it will be parsed wrong, but not until whole statement would be analyzed.
Speaking of functional cast, typename(name) syntax may appear in declarations of variables or types, which result in following being a legal declaration:
int foo ( int (a) )
{
return a;
}
It's because since times of C we can declare some eldritch array of pointers as void (*(*f[])())(), which declares f as array of pointers to function returning a pointer to function returning void.
In result, initializing a variable with a functional cast may create an ambiguous code:
float fvar = 1.0f;
double dvar ( int(fvar) );
It declares a double(int) function named dvar! You wouldn't spot it, until you try assign to it:
auto v = dvar; // v is a pointer to function.
dvar = 4; // error
The declaration of dvar as whole matches BOTH a function declaration and a variable declaration. In this case compiler MUST choose wrongly, but diagnostics are optional. It's exacerbated by fact that C and C++ are allowing variable identifiers to match type names.
2) Initialization of object
Yes, the expression test(1) is a functional cast even if test is a class. By definition it results in considering an existing constructor to be called.
class test {
public:
test(int arg): b(arg) {}
protected:
int b;
};
Here constructor test(int) is called a conversion constructor as it can take exactly one parameter and its usage permits use of type-id test in functional cast - from int or int-compatible type. Constructors with more than one parameter without default value do not bear this name. For class test , te expression test(1,2) is ill-formed.
In fact, unless such constructor qualified as explicit, it would allow an implicit conversion:
struct test {
test(int a) {std::cout << "int " << a << std::endl;}
explicit test (float a) {std::cout << "float " << a << std::endl;}
};
int main()
{
test b = 1; // calls (int) constructor
test c = 2.0f; // calls (int) constructor
test d = test(3.0f); // function cast calls (float) constructor
}
P.S. C++11 allowed to escape functional cast syntax for initialization of class-type object. For above class it would be test{1}. But it also can be an aggregate initialization, if test would be trivial:
struct test {
int b;
};
test t = test{1};
New Operator
Yes. From the cppreference page on the new keyword
The object created by a new-expression is initialized according to the following rules:
For non-array type, ...
If initializer is a parenthesized list of arguments, the object is direct-initialized.
And if we check out the page on direct initialization, we find that syntax (3) on that page is
T ( other )
T ( arg1, arg2, ... )
initialization ... by functional cast or with a parenthesized expression list
So new is defined to perform direct initialization, and direct initialization is defined to perform a functional cast when necessary. There's more going on with regard to allocation in the background, but at some point down the line a functional cast may get performed.
More than 1 Expression
In regards to your second question, a "cast" is a conversion from one type to another. An initialization with one argument can be viewed as a cast, from the argument type T to the result type S. It makes no sense to think of test(1, 2) as a conversion from anything, since it takes two arguments and produces one value.
This question already has answers here:
Can I set a default argument from a previous argument?
(7 answers)
Closed 5 years ago.
For a default argument in C++, does the value need to be a constant or will another argument do?
That is, can the following work?
RateLimiter(unsigned double rateInPermitsPerSecond,
unsigned int maxAccumulatedPermits = rateInPermitsPerSecond);
Currently I am getting an error:
RateLimiter.h:13: error: ‘rateInPermitsPerSecond’ was not declared in this scope
Another argument cannot be used as the default value. The standard states:
8.3.6 Default arguments...
9 A default argument is evaluated each time the function is called with no argument for the corresponding
parameter. The order of evaluation of function arguments is unspecified. Consequently, parameters of a
function shall not be used in a default argument, even if they are not evaluated.
and illustrates it with the following sample:
int f(int a, int b = a); // error: parameter a
// used as default argument
No, that cannot work because the evaluation of function arguments is not sequenced. It also does not work because the standard does not allow it, but I guess that was obvious.
Use an overload instead:
void fun(int, int) {}
void fun(int i) {
fun(i, i);
}
I was looking for an logical explanation for why it is not allowed
This is actually a good question. The reason is that C++ does not mandate the order of evaluation of arguments.
So let's imagine a slightly more complex scenario:
int f(int a, int b = ++a);
... followed by ...
int a = 1;
f(a);
C++ does not mandate the order of evaluation of arguments, remember?
So what should be the value of b?
f(a) can evaluate to either:
f(1, 2), or
f(2, 2), depending on the order of evaluation of the arguments.
Thus the behaviour would be undefined (and even undefinable).
Further, consider what might happen when a and b were complex objects whose constructors and copy operators had side-effects.
The order of those side-effects would be undefined.
You cannot do things like that because the standard does not allow it. However since default arguments effectively just define new function overloads, you can get the desired effect by explicitly defining such an overload:
void RateLimiter(unsigned int rateInPermitsPerSecond,
unsigned int maxAccumulatedPermits);
inline void RateLimiter(unsigned int rateInPermitsPerSecond)
{ return RateLimiter(rateInPermitsPerSecond,rateInPermitsPerSecond); }
This shows that the standard forbidding this is half-hearted, as suggested by the language ("Consequently... shall not..."). They just did not want to go through the hassle of making this well defined with the same effect as what the explicit overload declaration would do: if desired, they could have specified that defaulted arguments are evaluated after explicitly provided ones, and from left to right. This would not have any influence on the rule that the evaluation order of argument expressions in a function call is unspecified (because default arguments do not correspond to such expressions; they are entirely separate and not even in the same lexical scope). On the other hand if (as they did) they preferred to disallow this, they could have just said "shall not" without need to justify themselves from some other rule (but maybe with explanatory footnote).
For a default argument in C++, does the value need to be a constant or will another argument do?
The default value of an argument cannot be another argument. However, that does not mean it has to be a constant. It can be the return value of a function call.
int getNextDefaultID()
{
static int id = 0;
return ++id;
}
struct Foo
{
Foo(int data, int id = getNextDefaultID()) : data_(data), id_(id) {}
int data_;
int id_;
};
int main()
{
Foo f1(10); // Gets the next default ID.
Foo f2(20, 999); // ID is specified.
}
I did a test here, but the output is a loop without ending, I don't know why.
Actually, I am doing another test, but when I wrote this, I don't understand how the loop occurred. It is output "ABC" repeatedly.
#include <map>
#include <string>
#include <iostream>
class test
{
public:
std::map <int, int> _b;
test();
test (std::map<int, int> & im);
~test();
};
test::test()
{
std::cout<<"abc";
_b.clear();
_b[1]=1;
test(_b);
}
test::test(std::map <int, int>& im)
{
std::cout<<im[1];
}
test::~test() {};
int main ()
{
test a;
}
The issue here is that the compiler interprets the statement
test(_b);
not as code that creates a temporary object of type test passing in parameter _b, but as a variable declaration for a variable named _b of type test, using the default constructor. Consequently, what looks like a piece of code that creates a temporary test object using the second constructor is instead recursively creating a new object of type test and invoking the constructor another time.
To fix this, you can give the variable an explicit name, such as
test t(_b);
This can only be interpreted as a variable of type test named t, initialized using the second constructor.
I have never seen this before, and I've been programming in C++ for years. Thanks for showing me yet another corner case of the language!
For an official explanation: According to the C++03 ISO spec, §6.8:
There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
(My emphasis). In other words, any time C++ could interpret a statement as either an expression (the temporary object cast) or as a declaration (of a variable), it will pick the declaration. The C++ spec explicitly gives
T(a);
As an example of a declaration, not a cast of a to something of type T.
This is C++'s Most Vexing Parse - what looks like an expression is instead getting interpreted as a declaration. I've seen the MVP before, but I have never seen it in this context.
the problem is from constructor you again calling the contructor test(_b)
test::test(){std::cout<<"abc";_b.clear();_b[1]=1;test(_b);}
here is what happens
everytime you call test(_b) it first calls default constructor test::test and it in turns calls the test(_b) and the loop goes on and on untill the stack overflows.
remove the test(_b) from the default constructor
I'm pretty sure that you are not actually "calling the constructor" since they are not directly callable IIRC. The legalese had to do with constructors not being named functions - I don't have a copy of the Standard handy or I might quote it. I believe what you are doing with test(_b) is creating an unnamed a temporary which invokes the default constructor again.
I'm not familiar with the particularities of the standard, but it may be that calling a constructor within a constructor is undefined. As such it could be compiler dependent. In this particular case it causes infinite recursion of your default constructor without ever calling your constructor with the map argument.
C++ FAQ 10.3 has an example with a constructor that has two parameters. If you add an int parameters to your second constructor such as test(map, int), it exhibits a somewhat normal behaviour.
For good form I would simply change test::test(std::map <int, int>& im) for test::testInit(std::map <int, int>& im), and test(_b) to testInit(_b).
I came across a rather strange case of overload resolution today. I reduced it to the following:
struct S
{
S(int, int = 0);
};
class C
{
public:
template <typename... Args>
C(S, Args... args);
C(const C&) = delete;
};
int main()
{
C c({1, 2});
}
I fully expected C c({1, 2}) to match the first constructor of C, with the number of variadic arguments being zero, and {1, 2} being treated as an initializer list construction of an S object.
However, I get a compiler error that indicates that instead, it matches the deleted copy constructor of C!
test.cpp: In function 'int main()':
test.cpp:17:15: error: use of deleted function 'C(const C &)'
test.cpp:12:5: error: declared here
I can sort of see how that might work - {1, 2} can be construed as a valid initializer for C, with the 1 being an initializer for the S (which is implicitly constructible from an int because the second argument of its constructor has a default value), and the 2 being a variadic argument... but I don't see why that would be a better match, especially seeing as the copy constructor in question is deleted.
Could someone please explain the overload resolution rules that are in play here, and say whether there is a workaround that does not involve mentioning the name of S in the constructor call?
EDIT: Since someone mentioned the snippet compiles with a different compiler, I should clarify that I got the above error with GCC 4.6.1.
EDIT 2: I simplified the snippet even further to get an even more disturbing failure:
struct S
{
S(int, int = 0);
};
struct C
{
C(S);
};
int main()
{
C c({1});
}
Errors:
test.cpp: In function 'int main()':
test.cpp:13:12: error: call of overloaded 'C(<brace-enclosed initializer list>)' is ambiguous
test.cpp:13:12: note: candidates are:
test.cpp:8:5: note: C::C(S)
test.cpp:6:8: note: constexpr C::C(const C&)
test.cpp:6:8: note: constexpr C::C(C&&)
And this time, GCC 4.5.1 gives the same error, too (minus the constexprs and the move constructor which it does not generate implicitly).
I find it very hard to believe that this is what the language designers intended...
For C c({1, 2}); you have two constructors that can be used. So overload resolution takes place and looks what function to take
C(S, Args...)
C(const C&)
Args will have been deduced to zero as you figured out. So the compiler compares constructing S against constructing a C temporary out of {1, 2}. Constructing S from {1, 2} is straight forward and takes your declared constructor of S. Constructing C from {1, 2} also is straight forward and takes your constructor template (the copy constructor is not viable because it has only one parameter, but two arguments - 1 and 2 - are passed). These two conversion sequences are not comparable. So the two constructors would be ambiguous, if it weren't for the fact that the first is a template. So GCC will prefer the non-template, selecting the deleted copy constructor and will give you a diagnostic.
Now for your C c({1}); testcase, three constructors can be used
C(S)
C(C const&)
C(C &&)
For the two last, the compiler will prefer the third because it binds an rvalue to an rvalue. But if you consider C(S) against C(C&&) you won't find a winner between the two parameter types because for C(S) you can construct an S from a {1} and for C(C&&) you can initialize a C temporary from a {1} by taking the C(S) constructor (the Standard explicitly forbids user defined conversions for a parameter of a move or copy constructor to be usable for an initialization of a class C object from {...}, since this could result in unwanted ambiguities; this is why the conversion of 1 to C&& is not considered here but only the conversion from 1 to S). But this time, as opposed to your first testcase, neither constructor is a template so you end up with an ambiguity.
This is entirely how things are intended to work. Initialization in C++ is weird so getting everything "intuitive" to everyone is going to be impossible sadly. Even a simple example as above quickly gets complicated. When I wrote this answer and after an hour I looked at it again by accident I noticed I overlooked something and had to fix the answer.
You might be correct in your interpretation of why it can create a C from that initializer list. ideone happily compiles your example code, and both compilers can't be correct. Assuming creating the copy is valid, however...
So from the compiler's point of view, it has two choices: Create a new S{1,2} and use the templated constructor, or create a new C{1,2} and use the copy constructor. As a rule, non-template functions are preferred over template ones, so the copy constructor is chosen. Then it looks at whether or not the function can be called... it can't, so it spits out an error.
SFINAE requires a different type of error... they occur during the first step, when checking to see which functions are possible matches. If simply creating the function results in an error, that error is ignored, and the function not considered as a possible overload. After the possible overloads are enumerated, this error suppression is turned off and you're stuck with what you get.
Can anyone explain why the following code does not compile (on g++ (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-49))?
struct X {
public:
enum State { A, B, C };
X(State s) {}
};
int main()
{
X(X::A);
}
The message I get is:
jjj.cpp: In function 'int main()':
jjj.cpp:10: 'X X::A' is not a static member of 'struct X'
jjj.cpp:10: no matching function for call to 'X::X()'
jjj.cpp:1: candidates are: X::X(const X&)
jjj.cpp:5: X::X(X::State)`
Is this bad code or a compiler bug?
Problem solved by Neil+Konrad. See the comments to Neil's answer below.
You've forgot the variable name in your definition:
int main()
{
X my_x(X::A);
}
Your code confuses the compiler because syntactically it can't distinguish this from a function declaration (returning X and passing X::A as an argument). When in doubt, the C++ compiler always disambiguates in favour of a declaration.
The solution is to introduce redundant parentheses around the X since the compiler forbids parentheses around types (as opposed to constructo calls etc.):
(X(X::A));
X(X::A);
is being seen a s a function declaration. If you really want this code, use:
(X)(X::A);
Just to make it crystal clear what happens. Look at this example
int main() {
float a = 0;
{
int(a); // no-op?
a = 1;
}
cout << a;
}
What will it output? Well, it will output 0. The int(a) of above can be parsed in two different ways:
Cast to int and discard the result
Declare a variable called a. But ignore the parentheses around the identifier.
The compiler, when such a situation appears where a function-style cast is used in a statement and it looks like a declaration too, will always take it as a declaration. When it can't syntactically be a declaration (the compiler will look at the whole line to determine that), it will be taken to be an expression. Thus we are assigning to the inner a above, leaving the outer a at zero.
Now, your case is exactly that. You are trying (accidentally) to declare an identifier called A within a class called X:
X (X::A); // parsed as X X::A;
The compiler then goes on to moan about a not declared default constructor, because the static, as it assumes it to be, is default constructed. But even if you had a default constructor for X, it of course is still wrong because neither A is a static member of X, nor a static of X can be defined/declared at block scope.
You can make it not look like a declaration by doing several things. First, you can paren the whole expression, which makes it not look like a declaration anymore. Or just paren the type that is cast to. Both of these disambiguations have been mentioned in other answers:
(X(X::A)); (X)(X::A)
There is a similar, but distinct ambiguity when you try to actually declare an object. Look at this example:
int main() {
float a = 0;
int b(int(a)); // object or function?
}
Because int(a) can be both the declaration of a parameter called a and the explicit conversion (cast) of the float-variable to an int, the compiler decides again that that is a declaration. Thus, we happen to declare a function called b, which takes an integer argument and returns an integer. There are several possibilities how to disambiguate that, based on the disambiguation of above:
int b((int(a))); int b((int)a);
You should declare an object as
X x(X::A);
Bug in your code.
Either of these two lines work for me:
X obj(X::A);
X obj2 = X(X::A);
As Neil Butterworth points out, X(X::A) is being treated as a function declaration. If you really want an anonymous object, (X)(X::A) will construct an X object and immediately delete it.
You could, of course, just do something like this:
int main()
{
// code
{
X temp(X::A);
}
// more code
}
This would be more readable and basically have the same effect.