Rationale behind user provided ctor requirement for const objectsof non-POD class types

N

Nikos Chantziaras

(Originally posted to comp.std.c++, but that newsgroup is dead.)

Recently, I've seen a compiler (clang) that will not compile code like this:

struct A {
A() {}
};

class B {
A a_;
};

int foo()
{
const B b;
return 0;
}

It will not compile it because 'b' is const B, which does not have a
user defined ctor. C++'03 [dcl.init] p9 says:

If no initializer is specified for an object, and the object
is of (possibly cv-qualified) non-POD class type (or array
thereof), the object shall be default-initialized; if the
object is of const-qualified type, the underlying class type
shall have a user-declared default constructor.

Why is that? Why can't a const B constructed the same way as a
non-const B, using the implicitly defined default constructor?
 
A

Alf P. Steinbach /Usenet

* Nikos Chantziaras, on 23.05.2011 11:07:
(Originally posted to comp.std.c++, but that newsgroup is dead.)

Recently, I've seen a compiler (clang) that will not compile code like this:

struct A {
A() {}
};

class B {
A a_;
};

int foo()
{
const B b;
return 0;
}

It will not compile it because 'b' is const B, which does not have a user
defined ctor. C++'03 [dcl.init] p9 says:

If no initializer is specified for an object, and the object
is of (possibly cv-qualified) non-POD class type (or array
thereof), the object shall be default-initialized; if the
object is of const-qualified type, the underlying class type
shall have a user-declared default constructor.

Why is that? Why can't a const B constructed the same way as a non-const B,
using the implicitly defined default constructor?

Bjarne Stroustrup's "The Design & Evolution of C++" is a good source of answers
to such questions.

Unfortunately I don't have it.

But often these rationales are nothing but common sense. So I guess if you check
out D&E, and if it gives a rationale, it will be that

this unchanging "value" shall have been explicitly defined by the source code,
because generally it makes no sense to /name a value/ and not define that value
explicitly.

There are times where it might makes sense (if had been allowed) to write e.g.

int const someIntValue;

but from the compiler's point of view this seems like the programmer had
forgotten to supplyh the value to be named, and anyway in such cases it's no big
hardship to explicitly state what that value is,

int const someIntValue = int();

And likewise in your example.


Cheers & hth.,

- Alf
 
N

Nikos Chantziaras

* Nikos Chantziaras, on 23.05.2011 11:07:
[...]
C++'03 [dcl.init] p9 says:

If no initializer is specified for an object, and the object
is of (possibly cv-qualified) non-POD class type (or array
thereof), the object shall be default-initialized; if the
object is of const-qualified type, the underlying class type
shall have a user-declared default constructor.

Why is that? Why can't a const B constructed the same way as a
non-const B,
using the implicitly defined default constructor?
[...]
But often these rationales are nothing but common sense. So I guess if
you check out D&E, and if it gives a rationale, it will be that
this unchanging "value" shall have been explicitly defined by the source
code, because generally it makes no sense to /name a value/ and not
define that value explicitly.

There are times where it might makes sense (if had been allowed) to
write e.g.

int const someIntValue;

but from the compiler's point of view this seems like the programmer had
forgotten to supplyh the value to be named, and anyway in such cases
it's no big hardship to explicitly state what that value is,

int const someIntValue = int();

And likewise in your example.

Good thinking, but it doesn't seem to apply. Consider this:

#include <iostream>

class A {
int x_;
public:
A() : x_(5) { }
int x() const { return x_; }
};

class B: public A { };

int main()
{
const B b;
std::cout << b.x() << "\n";
}

With compilers that accept this, the output of the program is "5". But
the C++ standard says that it should not be compilable. In this case
however, 'b' has a perfectly fine initial state.

Also, let's not forget that an object can have mutable members, so the
requirement for a user defined ctor for const B makes even less sense.
 
A

Alf P. Steinbach /Usenet

* Nikos Chantziaras, on 23.05.2011 12:32:
* Nikos Chantziaras, on 23.05.2011 11:07:
[...]
C++'03 [dcl.init] p9 says:

If no initializer is specified for an object, and the object
is of (possibly cv-qualified) non-POD class type (or array
thereof), the object shall be default-initialized; if the
object is of const-qualified type, the underlying class type
shall have a user-declared default constructor.

Why is that? Why can't a const B constructed the same way as a
non-const B,
using the implicitly defined default constructor?
[...]
But often these rationales are nothing but common sense. So I guess if
you check out D&E, and if it gives a rationale, it will be that
this unchanging "value" shall have been explicitly defined by the source
code, because generally it makes no sense to /name a value/ and not
define that value explicitly.

There are times where it might makes sense (if had been allowed) to
write e.g.

int const someIntValue;

but from the compiler's point of view this seems like the programmer had
forgotten to supplyh the value to be named, and anyway in such cases
it's no big hardship to explicitly state what that value is,

int const someIntValue = int();

And likewise in your example.

Good thinking, but it doesn't seem to apply. Consider this:

#include <iostream>

class A {
int x_;
public:
A() : x_(5) { }
int x() const { return x_; }
};

class B: public A { };

int main()
{
const B b;
std::cout << b.x() << "\n";
}

I think this is completely like the `someIntValue` case. You want any value of
class `B`, or perhaps the default value. Then for this special case it's no big
deal to write

B const b = B();

just as with the `someIntValue`.

With compilers that accept this, the output of the program is "5". But the C++
standard says that it should not be compilable. In this case however, 'b' has a
perfectly fine initial state.

Also, let's not forget that an object can have mutable members, so the
requirement for a user defined ctor for const B makes even less sense.

As I see it mutable members are pretty irrelevant, because their mutability
should not affect the externally visible state. E.g., a mutable member might be
used for caching for some costly-to-compute result, for example a matrix
determinant, but the using code does not see any state change. Thus, declaring a
`const` object of such a well-designed class is no different; it is conceptually
to name an unchanging value, which "should" be specified.


Cheers & hth.,

- Alf
 
B

Balog Pal

Alf P. Steinbach /Usenet said:
I think this is completely like the `someIntValue` case. You want any
value of class `B`, or perhaps the default value. Then for this special
case it's no big deal to write

B const b = B();

Provided B is copyable :-((. The "can e declaration" bullshit takes its
usual toll. Normally you'd say

B const b();

but that is interpreted as a function.

At least we know the rationalte for that one: inherited from C without
change. Just like for() scoping this should be revised too.

I tried the b{} form with the code example upstream in current Cameau, no
luck. Not sure whether it is the mandated behavior or just the brace init is
not yet implemented.
 
Ö

Öö Tiib

Provided B is copyable :-((.  The "can e declaration" bullshit takes its
usual toll. Normally you'd say

 B const b();

but that is interpreted as a function.

So you need non-copyable, immutable and default-constructed instance.

If such need is common then the class may be made to provide reference
to one with a static member function. I sometimes do it for "null"
objects.

B const& b = B::none();

If you really need separate instance (why?) then aren't there some
syntax that will be not interpreted as function like 'B const b(());'
or something?
 
A

Alf P. Steinbach /Usenet

* Öö Tiib, on 25.05.2011 01:04:
So you need non-copyable, immutable and default-constructed instance.

If such need is common then the class may be made to provide reference
to one with a static member function. I sometimes do it for "null"
objects.

B const& b = B::none();

If you really need separate instance (why?) then aren't there some
syntax that will be not interpreted as function like 'B const b(());'
or something?

I think that with C++03 rules

B const& b = B();

should do it, while with C++98 this would (as I recall) require a copy
constructor to be accessible.

But I don't have time to check out the standard, sorry.


Cheers,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top