Topic: const' in header files


Author: lars.farm@nts.mh.se (Lars Farm)
Date: 1996/03/07
Raw View
In article <31379F8F.7659@compuserve.com>,
"John I. Moore, Jr." <70672.1744@compuserve.com> wrote:

>I think that there may be some misunderstanding about what a const data
>member actually represents.

No. I'm not talking about member constants.

>In general, a const defined at file scope can be
>of any type.  In C++, it is generally preferable to use const instead of
>#define.
>
> const int maxSize = 100;   //preferable to "#define maxSize 100"

Yes and I think this is standard practice. People rely on the fact that
unused constants are optimized away. Unfortunately, it appears that some
compiler writers do this only for the special case of integer types. I see
no reason why this should be any different:

  const float F = 1.234;     //should be preferable to "#define F 1.234"

Isuggested that required behaviour was that these constants are not
instantiated unless actually used, much like templates. Rather than leaving
it as an optional optimization as it appears to be now. This should apply
to any built in type. Thus invalidating stupid warnings about unused
variables. If some compilers warn about this, and according to the thread
in c.l.c++.moderated some do, then many users will have to revert to the
#define style constants instead. Is that the intent of the language
designers?


--
Lars Farm, lars.farm@nts.mh.se


[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: yeager@aosi.com
Date: 1996/03/08
Raw View
In <AD64B66E966816216@sleipner.nts.mh.se>, lars.farm@nts.mh.se (Lars Farm)
writes:

>I suggested that required behaviour was that these constants are not
>instantiated unless actually used, much like templates. Rather than leaving
>it as an optional optimization as it appears to be now. This should apply
>to any built in type. Thus invalidating stupid warnings about unused
>variables. If some compilers warn about this, and according to the thread
>in c.l.c++.moderated some do, then many users will have to revert to the
>#define style constants instead. Is that the intent of the language
>designers?

One area where such a requirement conflicts with existing rules would
be if the constant object has a constructor.  Then the existing rules
would not allow the constant to be omitted since the constructor must
be called (especially if it has detectable side effects) even if the
constant is not used in the particular compilation unit.

John Yeager
yeager@aosi.com
---
[ To submit articles: try just posting with your news-reader.
                      If that fails, use mailto:std-c++@ncar.ucar.edu
  FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu.
]





Author: aeb@saltfarm.bt.co.uk (Tony Bass)
Date: 1996/03/08
Raw View
>From article <4hn54s$3am@engnews1.Eng.Sun.COM>, by clamage@Eng.Sun.COM (Steve Clamage):

[... other interesting discussion]
> I suppose I should also comment on the comparison with unneeded template
> instantiations. That isn't an optimization issue, but a correctness issue.
> Simple example:
>  template< class T > class X {
>   T t;
>   ...
>   bool operator<( const T& rhs ) { return t < rhs.t; }
>  };
> Suppose we instantiate X on a type T for which the "<" operator is not
> defined. That isn't a problem unless we invoke X<T>::operator<. So the
> language rule is that a compiler can't reject a program due to an
> invalid instantiation unless the instantiation is actually required.
[...]


Presumably this language rule also covers the situation below with an
incomplete type, and my C++ compiler (HP-UX cfront-based) is incorrect
to reject it?


I had an example where I needed only pointers,

   template<struct T> struct VBlock;
   struct VScratchRep;

   struct VScratch {
     VScratchRep *chunk;
     VBlock<VScratchRep> *free;
    };

   int main()
   {
    VScratch *p = (VScratch *)0;
    return 0;
   }

which the compiler would accept in isolation, but if during the same
compilation it saw the definition of VBlock<T>::operator[],

   template<struct T> struct VBlock {
     T &operator[](int i);
     T *v;
    };

   template<struct T> inline T &VBlock<T>::operator[](int i)
   {
    return v[i];
   }

it failed, needing to know the size of VScratchRep even though [] was
never called in this compilation.  (I discovered this when including two
header files while compiling something else.)

    Tony Bass

--
# Tony Bass                                     Tel: (01473) 645305
# MLB 3/19, BT Laboratories                     e-mail: aeb@saltfarm.bt.co.uk
# Martlesham Heath, Ipswich, Suffolk, IP5 7RE   DO NOT e-mail to From: line
#                                               Opinions are my own
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: thp@cs.ucr.edu (Tom Payne)
Date: 1996/03/09
Raw View
Lars Farm (lars.farm@nts.mh.se) wrote:
:
: Experts explained that this is as it should be for const float, but not for
: const int (I know that the std does not talk about warnings). The point is:
: The experts seems to find some kind of conceptual difference between named
: constant floats and named constant ints, such that declaring const int K =
: L; in a header is considered good practice but const float X = Y; in a
: header is not and justifies a warning. I don't think there should be any
: such difference.

In fact, there is no such difference, as I understand it, in the way the
standard treats float and int in this regard.

Unused-variable warnings (a quality-of-implementation matter) are often
less than helpful, e.g., I get such a warning every time I acquire mutual
exclusion by creating an object whose constructor acquires the lock and
whose destructor releases it, since the object is not accessed elsewhere.

Tom Payne (thp@cs.ucr.edu)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "John I. Moore, Jr." <70672.1744@compuserve.com>
Date: 1996/03/01
Raw View
Lars Farm wrote:
>
> Experts explained that this is as it should be for const float, but not for
> const int (I know that the std does not talk about warnings). The point is:
> The experts seems to find some kind of conceptual difference between named
> constant floats and named constant ints, such that declaring const int K =
> L; in a header is considered good practice but const float X = Y; in a
> header is not and justifies a warning. I don't think there should be any
> such difference.

I think that there may be some misunderstanding about what a const data
member actually represents.  In general, a const defined at file scope can be
of any type.  In C++, it is generally preferable to use const instead of
#define.

 const int maxSize = 100;   //preferable to "#define maxSize 100"

Now consider const data members of a class.  First let's distinguish between
const data members and static const data members.  Const (non-static) data
members are certainly allowed in a class, but they are const relative to an
individual object.  Each object can have a different value for the const data
member.  Consider the following:

 class X
   {
     public:
         X(int);
         ...   // other members

     private:
         const int m_n;
         ...   // other members
   };

We could define the constructor as follows:

 X::X(int n)
   : m_n(n)
   {
     ...   // other statements
   }

Thus declarations such as

 X x1(5), x2(8);

would create two separate objects of class X, each having different values
of the const data member.  The data member is const after construction of the
object.

Now let's address data members which are const and static.  Such a data
member would be have the same const value for all objects of the class.
Consider the following:

 class X
   {
     public:
         X();
         ...   // other members

     private:
         static const int m_n;
         ...   // other members
   };

The static const data member can be defined and initialized once outside the
class definition, usually in a separate source code file (rather than the
header file).

 const X::m_n = 3;

Now all objects of the class see the same value (3) for the static const data
member.

All of the above discussion applies to the C++ described in Stroustrup's
1991 book as well as to the current draft standard.  What has changed with
the standard is the ability to actually define and initialize the static
const data member within the class definition.

 class X
   {
     public:
         X();
         ...   // other members

     private:
         static const int m_n = 3;
         ...   // other members
   };

Before this modification, one had to use enums within the class definition

 class X
   {
     public:
         X();
         ...   // other members

     private:
         enum { int m_n = 3 };
         ...   // other members
   };

or else one had to use a constant declared at file scope

 const int m_n = 3;

 class X
   {
     public:
         X();
         ...   // other members

     private:
         ...   // other members
   };

The restriction placed in the standard is that the ability to define and
initialize the static const data member within the class definition is
restricted to integral types.  You can't do it, for example, with doubles or
user-defined types.  As for the reasoning behind this restriction, you'll
have to ask someone on the standardization committee, but I suspect that it
has something to do with efficiency.  By the way, you should know that
Stroustrup opposed this modification even for integral types since it could
be accomplished within the existing language by the use of enums as
illustrated above.

--
John I. Moore, Jr.      phone:  (301) 924-0680
SoftMoore Consulting    email:  70672.1744@compuserve.com
16233 Monty Court
Rockville, MD  20853-1344
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]