Thread: More questions about constants

  1. #1
    Registered User
    Join Date
    Feb 2003
    Posts
    596

    More questions about constants

    1. There seem to be a number of opinions about how a const int (NOT a class member) should be declared & defined.

    For example, in this thread
    http://cboard.cprogramming.com/showt...ts+header+file
    Elysia says "Don't put any variables in headers. Define them in a .cpp file and use extern in the header files."

    In this one, Daved recommended defining a constant in a header file but enclosed in an unnamed namespace, ChaosEngine says to define it in a header file (wrapped in a #ifndef ... #endif block) as static const int, and CornedBee says that neither the static nor the namespace is necessary.
    http://cboard.cprogramming.com/showt...nstants+header

    I didn't find any clear statement on this from either Salem or Prelude. I'd love to get some further guidance on this -- is a consensus possible?


    2. Sort of related to the above, does "static" have any significance in C++ when used OUTSIDE of any class definition? Is there any difference between a global "static const int x" vs. a global "const int x"?


    3. This
    http://www.parashift.com/c++-faq-lit....html#faq-29.7
    sums up the reasons to prefer using const over #define, but it also says that there are cases when #define is preferable. So, when is #define preferable?

  2. #2
    Its hard... But im here swgh's Avatar
    Join Date
    Apr 2005
    Location
    England
    Posts
    1,688
    #define is the C version of a C++ constant. I would say the only time I would use #define is in a header file. I cannot find a reason why you would need to use it in a .cpp source file. constants more or less replaced #define in the C++ language when defining a variable to be constant ( read only ).

    Every book I have read says #define on a variable is "evil" but that is their view not every single programmers. Just my two cents on the matter.
    Double Helix STL

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by R.Stiltskin View Post
    For example, in this thread
    http://cboard.cprogramming.com/showt...ts+header+file
    Elysia says "Don't put any variables in headers. Define them in a .cpp file and use extern in the header files."
    The reason I mentioned that is because you'll get linking errors if you put variables in headers directly.
    There may be several approaches to this, but what I'd do is use define in a header or make the constants in a .cpp file and declare them as extern in the header files.
    That way you avoid linking errors.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> In this one, Daved recommended defining a constant in a header file but enclosed in an unnamed namespace.

    Please note that I wasn't recommending doing that as a solution to the problem. I was merely stating that if you choose to make a static const int at namespace scope that you should use an unnamed namespace instead.

    I'd trust CornedBee's recommendation to use neither static nor an unnamed namespace. I am not sure whether "extern" is required to avoid linking errors if you are declaring a constant.

    >> 2. does "static" have any significance in C++ when used OUTSIDE of any class definition?
    Yes. It means that the variable (or constant) is created for that particular compilation unit. If you put it in a source file, then functions in that source file can access it and you will not have any problems if there are similar names in other compilation units.

    If you put it in a header file, it acts the same way, except it does so for each source file that includes that header (including the source files that include the header indirectly). This is why CornedBee mentioned that using static for a constant has the disadvantage of creating a duplicate for each source file that includes the header where it was declared.

    Note that using static this way is deprecated in favor of an unnamed namespace, as I mentioned in the linked thread.

    >> So, when is #define preferable?
    I don't know of any situation where it would be preferable when a const variable or enum will also work. You can use #defines for pre-processor things like header include guards or conditional compilation.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Daved pointed out that "the use of the static keyword is deprecated when declaring objects in namespace scope", so that rules out ChaosEngine's suggestion. I note that the C++ Standard states in section 7.3.1.1: "The use of the static keyword is deprecated when declaring objects in a namespace scope (see annex D); the unnamed-namespace provides a superior alternative." This leads to Daved's suggestion, but I believe CornedBee is right: the unnamed/anonymous namespace means that there is a constant with the same name and value but in a different namespace in each file, thus it is duplicated across the program.

    Consequently, I think that the use of extern as suggested by Elysia may be the best option, possibly when in a namespace. (At least excluding other options like enums and class members.)
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Thanks to all of you.

    I did some testing (not that I don't trust you guys) & it's clear that static in this context gives the variable file scope so when I put static int x in a header file & #include that header in 2 different source files, each source file gets a different variable named x whose value can be changed in one sourcefile & remain unchanged in the other. So, as laserlight & Daved said, putting a constant static const int x = 5 in a header & #including that in multiple source files results in multiple constants all named x with the value 5 & just wastes space (unless for some reason we want different variables named x with different values at different places in a program). I don't understand the point of an unnamed namespace -- static gives this behavior without any namespace in the code.

    I also found that if the variable is not static, header include guards provide no protection (I thought they would); when I #include a header that defines int j = 8 in two different source files, g++ gives a "multiple definition" error. Thinking about it, the reason is obvious -- if that header is used to compile two separate object files it's going to create a separate global variable in each of them so clearly that creates a problem when it gets to the linker. I'm surprised I haven't had a problem with this before -- just lucky I guess.

    So Elysia's solution seems to be the best since it avoids the compiler's multiple definition errors, allows selective inclusion in only the source files where the variable (constant or otherwise) is needed, and avoids using the "nasty" #define.

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> I don't understand the point of an unnamed namespace -- static gives this behavior without any namespace in the code.
    And an unnamed namespace gives that behavior without any static in the code. The short answer is that you should prefer an unnamed namespace over static because the C++ standard tells you to. I don't know the long answer of why the standards committee made that decision without looking it up again. (Also remember that I'm only comparing instances where you will be using either static or an unnamed namespace, which is not the case in the example you are discussing in this thread).

    >> I also found that if the variable is not static, header include guards provide no protection
    A header include guard provides protection against compilation errors, since it stops a header from being included twice in the same compilation unit. It does not stop linker errors, since those come from the linker trying to combine the code in different compilation units.

    The question I still have is whether the extern is necessary for constants.

  8. #8
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Quote Originally Posted by Daved View Post
    The question I still have is whether the extern is necessary for constants.
    It seems not to be. Actually, I have no trouble getting it to work without extern, and I was also able to compile with extern const int j declared in a header and defined in the "main" source file. But I can't figure out how to compile with the constant declared as extern in a header and defined in a separate source file. This set of files gives me two "undefined reference to 'j'" errors. (It compiles all 3 ".o" files, but doesn't link them.) Can you tell me what's wrong?

    Code:
    #ed: commented out the following 2 lines as they were not serving any purpose in this makefile
    #CXX = g++
    #CCFLAGS = -g -Wall
    
    scopetest:	source1.o	source2.o	constants.o
    	g++	source1.o	source2.o	constants.o	-o scopetest
    
    source1.o:	header1.h	header2.h	source1.cc
    	g++	-c	source1.cc
    
    source2.o:	header1.h	header2.h	source2.cc
    	g++     -c	source2.cc
    
    constants.o:	constants.cc
    	g++	-c	constants.cc
    
    clean:
    	rm *.o scopetest
    Code:
    // header1.h
    
    #ifndef header1_h
    #define header1_h
    
    static int i = 4;
    extern const int j;
    //const int j = 6;
    
    int dosomething();
    
    #endif
    Code:
    // header2.h
    
    namespace {
    const int c = 5;
    }
    Code:
    // source1.cc
    
    #include "header1.h"
    #include "header2.h"
    #include <iostream>
    using namespace std;
    
    //const int j = 6;
    
    int main() {
      dosomething();
      ++i;
      dosomething();
      cout << "in main, i = " << i << endl;
      cout << "in main, c = " << c << endl;
      cout << "in main, j = " << j << endl;
      dosomething();
    
    }
    Code:
    // source2.cc
    
    #include "header1.h"
    #include "header2.h"
    #include <iostream>
    using namespace std;
    
    int dosomething() {
      cout << "in source2.cc, i = " << i << endl;
      cout << "in source2.cc, c = " << c << endl;
      cout << "in source2.cc, j = " << j << endl;
      return i;
    }
    Code:
    // constants.cc
    
    const int j = 6;
    Last edited by R.Stiltskin; 01-02-2008 at 10:51 PM.

  9. #9
    Registered User
    Join Date
    Feb 2003
    Posts
    596
    Duh!

    I needed #include "header1.h" in constants.cc. It should be
    Code:
    // constants.cc
    
    #include "header1.h"
    
    const int j = 6;
    With that correction it compiles & runs correctly. Or, I can eliminate constants.cc and constants.o from the makefile and in header1.h replace
    Code:
    extern const int j;
    with
    Code:
    const int j = 6;
    and it compiles & runs correctly that way as well.

    So, as Daved suggested, it seems that extern is not necessary for a constant.

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Code:
    // a1.cc
    #include <iostream>
    
    const int x = 5;
    
    int main()
    {
       std::cout << x << std::endl;
    }
    Code:
    // a2.cc
    const int x = 6;
    Compile and link these two together.

    1) Does you compiler/linker allow it?
    2) If so, what value does it print?
    3) Explain.
    4) Add a function to a2.cc which prints x, and call that from a1.cc. What happens?

    This exercise should help you figure out how C++ treats "const" at global scope.
    Last edited by brewbuck; 01-02-2008 at 10:58 PM.

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    The reason I suggest not making the constant extern is related to what brewbuck just posted. Basically, if the compiler can't see the value of the constant, it will have to treat it as a variable as far as the generated code is concerned. It's still a constant, but it's no longer a "constant expression", as the C++ standard calls it. That's the case in Elysia's way.
    By making the value visible, the compiler can use it directly, use it in template instantiations, in array sizes, put it as a literal in the code, do compile-time partial calculations, loop unrolling, dead code elimination and all that jazz.

    E.g.
    Code:
    // hdr.hpp
    #ifndef HDR_H_SR_H
    #define HDR_H_SR_H
    
    extern int CON1;
    const int CON2 = 0;
    
    #endif
    
    // src.cpp
    #include <iostream>
    #include "hdr.hpp"
    
    int main()
    {
      if(CON1) {
        std::cout << "Con1\n";
      }
      if(CON2) {
        std::cout << "Con2\n";
      }
    }
    The CON2 branch is optimized away by the compiler, the CON1 branch isn't.
    Last edited by CornedBee; 01-03-2008 at 04:51 AM.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by CornedBee View Post
    Code:
    // hdr.hpp
    extern const int CON1;
    How about extern const int. Regardless if it's in the current scope or not, the compiler can see that it's constant.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    A typo in my test program. I just repeated it with the const, and the result is the same.
    Of course, a compiler supporting whole-program optimization might discover this, too. Not all compilers do, though, and WPO is very expensive in compile time.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Nope. VC9 kills both for me. Even if it's "extern."
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    hmm... section 7.1.5.1 of the C++ Standard reads:
    An object declared in namespace scope with a const-qualified type has internal linkage unless it is explicitly declared extern or unless it was previously declared to have external linkage. A variable of nonvolatile const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions (5.19).
    This confirms what CornedBee is talking about concerning "all that jazz". What I find interesting though is the mention of "internal linkage". Since I am not very clear on this concept, I checked out section 3.5:
    A program consists of one or more translation units (clause 2) linked together. A translation unit consists of a sequence of declarations.
    Code:
    translation-unit:
        declaration-seqopt
    A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope:
    • When a name has external linkage, the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.
    • When a name has internal linkage, the entity it denotes can be referred to by names from other scopes in the same translation unit.
    • When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.

    A name having namespace scope (3.3.5) has internal linkage if it is the name of
    • an object, reference, function or function template that is explicitly declared static or,
    • an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage; or
    • a data member of an anonymous union.
    So, without the extern the constant has internal linkage. Doesn't this mean that we have the same scenario as with static or the unnamed namespace where each source file (or more precisely, translation unit) has its own copy of the constant?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C interview questions
    By natrajdreams in forum C Programming
    Replies: 7
    Last Post: 12-12-2010, 12:40 PM
  2. questions....so many questions about random numbers....
    By face_master in forum C++ Programming
    Replies: 2
    Last Post: 07-30-2009, 08:47 AM
  3. A very long list of questions... maybe to long...
    By Ravens'sWrath in forum C Programming
    Replies: 16
    Last Post: 05-16-2007, 05:36 AM
  4. Several Questions, main one is about protected memory
    By Tron 9000 in forum C Programming
    Replies: 3
    Last Post: 06-02-2005, 07:42 AM
  5. Trivial questions - what to do?
    By Aerie in forum A Brief History of Cprogramming.com
    Replies: 23
    Last Post: 12-26-2004, 09:44 AM