initialization of a const static float data member in a class

J

James Kanze

James Kanze wrote:
Interesting point. With current compilers, a declaration is
only required if the address of a static const member is used.
Presumably the value is a compile time constant, so the
declaration is not required in immediate uses.

In standardese: the definition is only required if there is a
use which doesn't result in an immediate lvalue to rvalue
conversion. Passing the object as an argument to
std::vector<>::push_back, or to any function taking a reference,
will still cause an error if no definition is present with g++
(and I suppose with most compilers).
With a floating point type, this will not be the case. So
which rules will apply in the new standard? If a declaration
is required for all uses, plenty of existing code will break.

Or will it? Formally, the object is used unless it "satisfies
the requirements for appearing in a constant expression (5.19)
and the lvalue-to-rvalue conversion (4.1) is immediately
applied." (According to the latest draft; in the current
standard, it is required if the object is used, period.) All of
the compilers I know do require the definition unless there is
an immediate lvalue-to-rvalue conversion. So 1) the draft
loosens the requirements some, making previously illegal code
legal, and 2) it does so in conformance with what current
applications actually require.

With regards to something like:

struct S { static double const d = 2.0 * 3.14 ; } ;

it does create an additional difficulty for the compiler if it
is incapable of folding floating point constants, but no more so
than much which is required in order to correctly implement
templates.
 
J

James Kanze

* James Kanze:
Right. It's specious. :)
Uh, note James, lately I've not had the drive I had earlier to
engage in all sorts of long-winded discussions.
I just make my points and if people don't grok 'em, well I
don't any longer feel any strong compulsion to enlighten them
-- let people be idiots, you can't change them, that's my new
motto! :)

I go through phases like that myself. It causes a lot less
stress.
But I make exception for you since you're not in
ordinary people (idiot) class, but somewhere at opposite end
of scale. Just, still don't expect me to reply to all.

No worry. Much of what I posted was not really an attempt to
disagree with you, but to put things in historical perspective
for other readers: why the current situation is the way it is.
If we didn't have to deal with history, I'm not sure how we'd
specify the language today. But I'm fairly certain that the
language would be specified in a way to allow initializer values
to be specified in the class, and probably in a way to not
require an additional definition if they were specified. For
better or for worse, however, the current specification is a
result of history, and a history in which C compatibility and
the ability to work with classical C linkers was important.

[...]
I think you mean "compile time constant".

More or less. Perhaps it would be more accurate to say that
there is no place in the standard which would require the
compiler to evaluate that expression.
Right. And irrelevant. Misleading though since we're talking
about (other) constants and it might seem to apply to those.
It doesn't.
Yeah, that one. Plus the one limiting in-class initialization.
It's not required to.

No. It's a violation of the one definition rule, which is
undefined behavior. I'm at home now, so I can only verify with
g++, but I'm pretty sure that Sun CC behaves similarly.

I've also just noticed that the current draft also modifies
this. In the standard, a definition is required if the variable
is used, period. In the latest draft I have at hand here, it is
required if the variable is used in a way that doesn't result in
an immediate lvalue-to-rvalue conversion, which corresponds
exactly to the behavior of the compilers I have at hand.
Yeah, that's stupid.

But with the above change, corresponds to the behavior of g++
and Sun CC (and probably most other compilers).
Here's one possible proposal, off the cuff, so not sure if
compiles! :)
The C++xx construction
struct A
{
static double const x = 1*2;
};
is to be treated as (i.e. compiler rewrites as) C++98 valid code
template< typename Dummy >
struct __A_x
{
static double const x;
};
template< typename Dummy >
double const __A_x<Dummy>::x = 1*2;
struct A: __A_x<void>
{
};
where __A_x is some generated globally unique name, and except
that if there is any way of generally detecting whether A has
a base class (dunno), then such way shall not detect the
presence of __A_x.

Well, that's hardly the language which would be used in the
standard, but it is a good proof of implementability (which is
also necessary---not that I doubted it, but you know how it is).

I rather liked Kai-Uwe's approach, although it still needs a
little work.
Since the rewritten code is valid C++98 code there's no
adjustment of one-definition-rule: it's still in effect as
before, and still rules.

What happens to code which defines the variable (as currently
required)? Does it suddenly become illegal?
If so, hurray (I haven't noticed that, but then I haven't
scrutinized the draft!) -- and if so, it sort of pulls the
rug under James' argument that
"For most of the members of the committee (and for all of the
other experts I know), [There is no imperative language based
reason to allow [these in-class definition initializations]].
The general feeling is that ideally, you shouldn't be able to
provide the initialization in the class definition at all"
You'll have to explain that one to me. There is no imperative
language based reason.
Yeah. And as mentioned earlier that applies to 'for', 'do' and
'while' as well. Your argument was that in spite of that,
most members of the committee and all other experts you know
had embraced the fallacious argument, so that they would not
be likely to consider anything like reason, but with some
support in C++0x draft, although still keeping separate
definition sillyness, that argument has very little force left
it in -- when they can be moved to go most of the way,
perhaps they can be moved to go the teeny tiny little distance
left also?

I think you misunderstood my claim. Given the current object
model (and the one definition rule, etc.), allowing any
initializer is a hack. C++ didn't allow it even for integers
for the longest time, and I'm aware of some experts who don't
like that exception (for whatever reasons). The "imperative
language based reasons" provided the impetus which made the hack
acceptable. (It's not like for, do or while, because they
aren't felt to be hacks.)
Of course, there is a huge distance left for the Most General
Solution, that of introducing something akin to 'inline' for
/data/. :) But since we don't really really need that MGS in
practice, no need to waste time convincing.

Actually, it might be a good idea. It expresses exactly what is
really wanted, and solves the problem with the one definition
rule.

Note that in the current draft, you can declare an inline
function to be a constexpr (with serious limitations as to what
you can do in the function, of course); even class types can be
constant expressions, if their non-copy constructors are
constexpr. So IMHO, there is a (new) very strong argument for
providing some sort of mechanism where you don't have to provide
a definition elsewhere: if

struct S { constexpr static inline int f() { return 43 ; } } ;

is legal, and you don't have to provide a definition for f
elsewhere, why should you not be able to do the same thing for a
data object?

Now if only there were enough time to discuss it more
thoroughly. (I seriously like the "inline" idea. The parallel
with functions appeals to me. I obviously can't speak for
the committee on this, but it also sounds like something simple
enough to have a chance of still slipping in. With a strong
argument on grounds of orthogonality: it works with functions,
why not with data members.)
 
K

Kai-Uwe Bux

James said:
James Kanze wrote:
[...]
For the moment, I don't know exactly what the
counter-proposal is, so it's hard to argue against it, or
agree with it. If the proposal is that the declaration in
the class should be a definition (and thus, that you don't
need a definition elsewhere), then I'm radically against it;
such a rule would mean that things like:
static int someArray[] ;
would be illegal (since a definition requires a complete
type), and that you'd always have to provide the initializer
in the class (and most of the time, you really don't want
to).
So let's try to reword [9.4.2/2]:
The declaration of a static data member in its class
definition is not a definition and may be of an incomplete
type other than cv-qualified void unless it specifies an
initializer, in which case it is a definition and the type
shall be complete. If the declaration is not a definition,
the definition for a static data member shall appear in a
namespace scope enclosing the member?s class definition. In
the definition at namespace scope, the name of the static
data member shall be qualified by its class name using the
:: operator. The initializer expression in the definition of
a static data member is in the scope of its class (3.3.6).
What about that?

It's a start. Of course, you'll also need something in §3.2.
Otherwise, a header which contains something like:

struct S { static int i = 43 ; } ;

will cause undefined behavior if it is included in more than one
translation unit.

True.
Also, consider the issues of backwards compatibility; your
suggestion would make a lot of currently conforming code
illegal (since the previously required definition would suddenly
become a duplicate definition).

Actually, I was more thinking about what people could have done 20 years
ago. Off the top of my head, I don't see how backward compatibility can be
achieved.
But those problems should be solvable as well. The real problem
is, of course, that on one has presented such a proposition to
the committee (and I suspect that it is too late now---we should
be very, very close to a CD, if the next version is really to be
C++0x, and not C++1x).

Probably it is not just too late but also not really worth the hassle. After
all, for the technical issues (inclusion of constants in header files) work
arounds do exists and all other issues are strictly matters of style and
taste.
You mean that you make almost all of your functions inline?
Yup.

In my work, it is normal to insist that the implementations be
in a separate file (even if, in the case of templates, this file
is included by the header). It's more or less a basic principle
if you're working in large systems (or more strictly speaking,
if there are more than two or three people working on the
application); you don't want the interface to accidentally
change after some bug fix in the implementation. (If you've
never accidentally changed something you didn't mean to, you're
a better typist than I am. Where as it's impossible to
accidentally change something in a file you haven't checked
out.)
[snip]

I agree that redundancy is good. It safeguards against accidental changes.
However, I think that what one really wants is not a redundancy within the
implementation (.h vs .cc) but redundancy that comes from the existence of
documentation. Ideally, I would have a tool that checks whether my
documented interface matches the implemented interface. I feel that the
current use of the compiler to ensure header and source file match is a
crutch that encourages confusing the header file for documentation.


Best

Kai-Uwe Bux
 
J

James Kanze

* James Kanze:

[...]
Couldn't, so this change would have to clean up that
untidyness (see, I'm also moderating my language! Heh) where
something with an initializer is currently regarded as a pure
declaration.

I'm not really sure I understand which direction you're arguing
here. You say (correctly, IMHO) that code which currently
provides the separate definition couldn't suddenly become
illegal, but they you want to clean up the untidiness in which
something with an initializer is a pure declaration (which is,
of course, what I've been calling a hack). I fear that no
matter how you do it, you're going to have to introduce another
hack to avoid breaking existing code.
That's, oops, almost introduced some Language there, but it's
a conceptual inconsistency that has to be explained to
everyone who reaches that level of understanding. Fixing that,
removing that special case aberration, so then "has
initializer" = "is definition", so that what looks like a
definition then is a definition just like everywhere else in
the language, then those silly-definitions that look like pure
declarations can just be pure declarations, and allowed as
redundant declarations for backwards compatibility.

OK. That sounds like a reasonably good fix for the problem.
(Not that they look like pure declarations. Something like "int
A::x ;" is a definition if it appears at namespace scope. Even
if A is a namespace, and not a class.)
So, this would also clean up the conceptual a bit!
By the way, since you liked "inline data" idea, that's
compatible with above proposal. But it's more general since it
would describe/conceptualize the above as an implicit inline
definition, just like member functions that are /defined/ in
the class definition. And then would presumably also allow
'inline' explicitly applied to data, complete orthogonality
for that.

Yes. That's probably what I liked most about it: its
orthogonality.
- Alf (who liked orhtogonal MC68000 much better than
special-cases i80386).

Didn't everyone?
 

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,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top