struct Foo {};
struct Bar : Foo {};
Foo &foo = Bar; // without ()
I wonder, Is it a legal notation? And if it is legal, could you give some details? Something like, Why it's legal? Or, What is the origin of such a notation?
EDIT: I cannot compile this code. But I met a code like that and wanted to know whether such a notation is allowed (probably just my compiler doesn't support this notation). I'm having some uncertainty since the following notation is quite legal: Foo *pFoo = new Bar;
It should be a compiler error.
g++: error: expected primary-expression before ';' token
Bar is a name of class and it cannot be assigned to reference / variable. Even with putting () it will not compile, unless you make foo a const Foo&.
You cannot assign an class Name to a reference/object. It is neither syntactically valid nor does it make any sense.
You cannot bind a reference to a temporary(rvalue), So following is illegal too:
Foo &foo = Bar();
You can bind a temporary(rvalue) to an const reference, So following is legal:
const Foo &foo = Bar();
The C++ standard specifically allows the 3.
The code as presented is not legal because Bar is the name of a class, not a variable.
The following, however, is:
struct Foo {}
struct Bar : Foo {}
Bar fooBar;
Foo &foo = fooBar; // without ()
It is legal because Bar is a Foo, so you're just giving a different name to your variable fooBar.
Note however that foo, although an alias for fooBar, will interpret the location as a Foo object.
This means the following:
struct Foo { int x; }; //note semicolons after struct declaration
struct Bar : Foo { int y; };
Bar fooBar;
fooBar.y = 2;
fooBar.x = 3;
Foo &foo = fooBar;
int aux;
aux = foo.x; // aux == 3
aux = foo.y; // compile error
You can't assign a value to a reference. So as already mentioned this is not legal regardless of the parentheses.
Related
Let's suppose that I have struct Foo with move constructor and operator=(Foo&&), and I used it as data member:
Foo f()
{
Foo foo;
//code
return foo;
}
struct Boo {
Foo foo;
Boo() {
foo = f();//1
foo = std::move(f());//2
}
};
In case (2) I actually not need std::move,
but what if I used it here, does this make something bad,
like preventing optimization?
I read this: Why does std::move prevent RVO?
and find out that changing return foo; to return std::move(foo); cause disabling of RVO, but what about (2) does it cause the similar situation? And if so, why?
It's redundant and confusing. Just because I can write std::add_pointer_t<void> instead of void*, or std::add_lvalue_reference_t<Foo> (or Foo bitand) instead of Foo&, doesn't mean I should.
It also matters in other contexts:
auto&& a = f(); // OK, reference binding to a temporary extends its lifetime
auto&& b = std::move(f()); // dangling
and so, if Foo is something that can be iterated over,
for(const auto& p : f()) {} // OK
for(const auto& p : std::move(f())) {} // UB
And in your example, if the assignment operator is implemented as copy-and-swap (operator=(Foo)), then foo = std::move(f()) forces a non-elidable move, while foo = f() can elide the move from f()'s return value to the argument of operator=.
Here is a code snippet:
typedef struct foo {
int i;
int o;
} foo;
int main() {
foo bar2 = {
.i = 42;
.o= 24;
};
foo bar1;
bar1.i = 42;
bar1.o = 24;
}
I am trying to initialize the variable bar2 within the declaration, but it didn't work. But the initialization of the struct bar1 works pretty fine.
Can Anybody tell me why the initialization of foo2 gives a SyntaxError?
The initializer elements must be separated with a comma, not a semicolon,
However while C (since C99) supports designated initializers, standard C++ does not support it. See this question.
You can do:
foo bar2 = { 42, 24 };
Some C++ compilers might support such a designated initializer list, which comes from C, as an extension - in which case the syntax would be:
foo bar2 = {.i = 42, .o= 24};
I ran into a strange issue.
Considering this example:
class Foo
{
static const int Bar = 5;
public:
Foo()
{
_map[Bar] = "some characters";
}
~Foo() {}
private:
std::map<int, std::string> _map;
};
int main()
{
Foo a;
return (0);
}
I get this error (compiling with g++ 4.7.2):
/tmp/ccLy806T.o: In function `Foo::Foo()':
Main.cpp:(.text._ZN3FooC2Ev[_ZN3FooC5Ev]+0x1e): undefined reference to `Foo::Bar'
Now, if I make a static_cast on Bar, it works:
Foo()
{
int i = Bar; //works
_map[static_cast<int>(Bar)] = "some characters"; //works
}
This error only appears when using Bar as map subscript in the constructor. Writing _map[Bar] = "some characters"; in an other function in the Foo class doesn't produce any error.
That's really strange for me, but I expect that someone here has an answer.
So, what am I doing wrong ?
That's because map::operator[] takes its key as a int const&. It wants the address of the thing you're passing into it. When you do:
_map[static_cast<int>(Bar)]
you're creating a temporary, and passing in the address to that temporary, which is fine. But when you're doing:
_map[Bar]
Bar doesn't actually have memory storage. You need to provide it via:
class Foo {
....
};
const int Foo::Bar;
You need to add the following at the top level to allocate storage for Foo::Bar:
const int Foo::Bar;
For a class Foo, is there a way to disallow constructing it without giving it a name?
For example:
Foo("hi");
And only allow it if you give it a name, like the following?
Foo my_foo("hi");
The lifetime of the first one is just the statement, and the second one is the enclosing block. In my use case, Foo is measuring the time between constructor and destructor. Since I never refer to the local variable, I often forget to put it in, and accidentally change the lifetime. I'd like to get a compile time error instead.
Another macro-based solution:
#define Foo class Foo
The statement Foo("hi"); expands to class Foo("hi");, which is ill-formed; but Foo a("hi") expands to class Foo a("hi"), which is correct.
This has the advantage that it is both source- and binary-compatible with existing (correct) code. (This claim is not entirely correct - please see Johannes Schaub's Comment and ensuing discussion below: "How can you know that it is source compatible with existing code? His friend includes his header and has void f() { int Foo = 0; } which previously compiled fine and now miscompiles! Also, every line that defines a member function of class Foo fails: void class Foo::bar() {}")
How about a little hack
class Foo
{
public:
Foo (const char*) {}
};
void Foo (float);
int main ()
{
Foo ("hello"); // error
class Foo a("hi"); // OK
return 1;
}
Make the constructor private but give the class a create method.
This one doesn't result in a compiler error, but a runtime error. Instead of measuring a wrong time, you get an exception which may be acceptable too.
Any constructor you want to guard needs a default argument on which set(guard) is called.
struct Guard {
Guard()
:guardflagp()
{ }
~Guard() {
assert(guardflagp && "Forgot to call guard?");
*guardflagp = 0;
}
void *set(Guard const *&guardflag) {
if(guardflagp) {
*guardflagp = 0;
}
guardflagp = &guardflag;
*guardflagp = this;
}
private:
Guard const **guardflagp;
};
class Foo {
public:
Foo(const char *arg1, Guard &&g = Guard())
:guard()
{ g.set(guard); }
~Foo() {
assert(!guard && "A Foo object cannot be temporary!");
}
private:
mutable Guard const *guard;
};
The characteristics are:
Foo f() {
// OK (no temporary)
Foo f1("hello");
// may throw (may introduce a temporary on behalf of the compiler)
Foo f2 = "hello";
// may throw (introduces a temporary that may be optimized away
Foo f3 = Foo("hello");
// OK (no temporary)
Foo f4{"hello"};
// OK (no temporary)
Foo f = { "hello" };
// always throws
Foo("hello");
// OK (normal copy)
return f;
// may throw (may introduce a temporary on behalf of the compiler)
return "hello";
// OK (initialized temporary lives longer than its initializers)
return { "hello" };
}
int main() {
// OK (it's f that created the temporary in its body)
f();
// OK (normal copy)
Foo g1(f());
// OK (normal copy)
Foo g2 = f();
}
The case of f2, f3 and the return of "hello" may not be wanted. To prevent throwing, you can allow the source of a copy to be a temporary, by resetting the guard to now guard us instead of the source of the copy. Now you also see why we used the pointers above - it allows us to be flexible.
class Foo {
public:
Foo(const char *arg1, Guard &&g = Guard())
:guard()
{ g.set(guard); }
Foo(Foo &&other)
:guard(other.guard)
{
if(guard) {
guard->set(guard);
}
}
Foo(const Foo& other)
:guard(other.guard)
{
if(guard) {
guard->set(guard);
}
}
~Foo() {
assert(!guard && "A Foo object cannot be temporary!");
}
private:
mutable Guard const *guard;
};
The characteristics for f2, f3 and for return "hello" are now always // OK.
A few years ago I wrote a patch for the GNU C++ compiler which adds a new warning option for that situation. This is tracked in a Bugzilla item.
Unfortunately, GCC Bugzilla is a burial ground where well-considered patch-included feature suggestions go to die. :)
This was motivated by the desire to catch exactly the sort of bugs that are the subject of this question in code which uses local objects as gadgets for locking and unlocking, measuring execution time and so forth.
As is, with your implementation, you cannot do this, but you can use this rule to your advantage:
Temporary objects cannot be bound to non-const references
You can move the code from the class to an freestanding function which takes a non-const reference parameter. If you do so, You will get a compiler error if an temporary tries to bind to the non-const reference.
Code Sample
class Foo
{
public:
Foo(const char* ){}
friend void InitMethod(Foo& obj);
};
void InitMethod(Foo& obj){}
int main()
{
Foo myVar("InitMe");
InitMethod(myVar); //Works
InitMethod("InitMe"); //Does not work
return 0;
}
Output
prog.cpp: In function ‘int main()’:
prog.cpp:13: error: invalid initialization of non-const reference of type ‘Foo&’ from a temporary of type ‘const char*’
prog.cpp:7: error: in passing argument 1 of ‘void InitMethod(Foo&)’
Simply don't have a default constructor, and do require a reference to an instance in every constructor.
#include <iostream>
using namespace std;
enum SelfRef { selfRef };
struct S
{
S( SelfRef, S const & ) {}
};
int main()
{
S a( selfRef, a );
}
No, I'm afraid this isn't possible. But you could get the same effect by creating a macro.
#define FOO(x) Foo _foo(x)
With this in place, you can just write FOO(x) instead of Foo my_foo(x).
Since the primary goal is to prevent bugs, consider this:
struct Foo
{
Foo( const char* ) { /* ... */ }
};
enum { Foo };
int main()
{
struct Foo foo( "hi" ); // OK
struct Foo( "hi" ); // fail
Foo foo( "hi" ); // fail
Foo( "hi" ); // fail
}
That way you can't forget to name the variable and you can't forget to write struct. Verbose, but safe.
Declare one-parametric constructor as explicit and nobody will ever create an object of that class unintentionally.
For example
class Foo
{
public:
explicit Foo(const char*);
};
void fun(const Foo&);
can only be used this way
void g() {
Foo a("text");
fun(a);
}
but never this way (through a temporary on the stack)
void g() {
fun("text");
}
See also: Alexandrescu, C++ Coding Standards, Item 40.
Not that I would ever write the code like the following in my professional work, the following code is legal and compiles without warnings in c++ and c:
#include <stdlib.h>
typedef struct foo { int foo; } foo;
foo * alloc_foo () {
return (struct foo*) malloc(sizeof(foo));
}
struct foo * alloc_struct_foo () {
return (foo*) malloc(sizeof(struct foo));
}
foo * make_foo1 (int val) {
foo * foo = alloc_struct_foo ();
foo->foo = 0;
return foo;
}
struct foo * make_foo2 (int val) {
struct foo * foo = alloc_foo();
foo->foo = 0;
return foo;
}
What makes this legal and unambiguous in C is section 6.2.3 of the C standard:
6.2.3 Name spaces of identifiers
If more than one declaration of a particular identifier is visible at any point in a translation unit, the syntactic context disambiguates uses that refer to different entities. Thus, there are separate name spaces for various categories of identifiers (label names; tags of structures, unions, and enumerations; members of structures or unions; and ordinary identifiers).
Note that thanks to label names living in their own name spaces, I could have made the code even more obfuscated by using a label foo somewhere.
Add the following and the code does not compile:
int foo (foo * ptr) {
return ++ptr->foo;
}
So, two questions, one related to C and C++ and the other, C++.
C/C++ question: Why can't I define the function foo?
It seems I should be able to define the function foo; function names and variable names are "ordinary identifiers". But if I add that last little bit of code I get error: redefinition of 'foo' as different kind of symbol.
Question: foo * foo; is perfectly legal, so why isn't int foo (foo*); legal?
C++ question: How does this work at all in C++?
The meaning of "name space" takes on a rather different meaning on in C++ than in C. I can't find anything in the C++ standard that talks about the C concept of name spaces, which is what makes the above legal in C.
Question: What makes this legal in C++ (chapter and verse preferred)?
foo * foo; is perfectly legal, so why isn't int foo (foo*); legal?
Because there already is a type named foo in the same declaration context as your function. You cannot have a type and a function of the same name in the same scope.
How does this work at all in C++?
Because you are allowed to hide names in nested scopes. When you declare foo * foo, the first foo refers to the type. The second foo declares a variable -- at that point, the type foo is hidden. Try declaring foo * baz after foo * foo, it should fail.
struct foo {};
void test() {
foo * foo; // first `foo` is the type, second `foo` is the variable
foo * baz; // first `foo` is the variable
}
In C++11 3.3.1/4 says that in a declarative region all declarations of a name must refer to the same entity (or an overload set). There's an exception that allows you to use a class name for a set of function names (so foo() hides class foo) but this doesn't apply if you have a typedef (which you do).
Try it with the typedef struct foo foo omitted in C++.
This doesn't work in C++ either ... the problem is that the pre-processor for gcc/g++ is looking for __cplusplus, not cplusplus. Therefore you pre-processor statements
#if defined FOO && ! defined cplusplus
#undef FOO
#endif
do not work correctly.
Because there is already function foo() it's a default constructof for struct foo
typedef struct foo
{
int a;
foo(int val)
:a(val)
{}
} foo;
int foo(int value)
{
cout << value <<endl;
}
void main()
{
foo foovar = foo(50); // constructor foo or function foo?
}
There are no such things as constructors in C.
Edit specially for Alan Stokes:
typedef struct foo
{
int a;
foo(int val, double val2)
:a(val)
{
cout << val2 << endl;
}
} foo;
int foo(int value, double val2)
{
cout << value << val2 << endl;
}
void main()
{
some_random_func(foo(50, 1.0)); // constructor foo or function foo?
}