1

Let say that I have two different versions of the same header foo.hpp, the first one:

// File foo.hpp
#ifndef FILE_FOO
#define FILE_FOO
namespace X
{
  static const int    i = 13;
  static const double d = 17.0;
}
#endif

and the second one:

// File foo.hpp
#ifndef FILE_FOO
#define FILE_FOO
struct X
{
  static const int    i = 13;
  static const double d = 17.0;
};
#endif

In the latter case the use of a structure is pointless, but I have done it on purpose to highlight my question. In both cases I have tried to build the following source file foo.cpp:

// File foo.cpp
#include "foo.hpp"
#include <iostream>
int main()
{
  std::cout << X::i << std::endl;
  std::cout << X::d << std::endl;
  return 0;
}

but only in the latter I get the following error:

In file included from foo.cpp:2:
foo.hpp:7: error: floating-point literal cannot appear in a constant-expression
foo.hpp:7: error: ISO C++ forbids initialization of member constant ‘d’ of non-integral type ‘const double’

I am using g++ 4.2.1 (so still with C++98 standard) and -pedantic, this option is strictly required to get the above error.

As discussed here, I can see the point of allowing only static constant integral or enumeration types to be initialized inside the class, because I guess the C++ standard does not specify how floating point should be implemented at compile time and it leaves it to the processor. But at this point the namespace case is misleading me...

Finally, the questions:

  • How does the compiler behave and translate the source into the object code in the above two cases?
  • Why does it give an error only with the second version of the header?

Thanks for your help!

Community
  • 1
  • 1
helmet_23
  • 405
  • 1
  • 5
  • 10

2 Answers2

1

I think you could find some useful information (maybe not a complete answer to your question..) in the following discussion: Why can't I have a non-integral static const member in a class?

Community
  • 1
  • 1
  • Thank you Christopher, I have seen that thread, but I can say that it's a bit different, because there is just one main source file and no headers. In that case you won't get any error nor any difference between `-O0` and `-O1`, at least with the `GCC 4.2.1` I have tried... – helmet_23 Mar 01 '16 at 10:47
  • 1
    Concerning your second question, I think that you could find interesting this discussion: [link](http://stackoverflow.com/questions/4726570/deprecation-of-the-static-keyword-no-more) Basically you should not be able to do what you are doing in the namespace based case... – Christopher23 Mar 04 '16 at 11:27
  • Ok, this answers the second question. Thanks – helmet_23 Mar 04 '16 at 11:59
1

Moreover, I think that this small example of namespace misuse could help us to shed some light on your first question (at least for the namespace case):

StaticNamespace.hpp:

#pragma once
namespace StaticNamespace
{
    static double d = 1.0;
}

Class.hpp:

#include "StaticNamespace.hpp"
#include <iostream>

class Class
{

public:

#if 1

    Class();

    void printFromSource() const;

#else

    Class(){
        StaticNamespace::d = StaticNamespace::d + 0.1;
    }

#endif

    void printFromHeader() const { std::cout<<"Class from header "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; }

    static void printDouble() { std::cout<<"Class static "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; }

};

Class.cpp:

#include "Class.hpp"

Class::Class()
{
    StaticNamespace::d = StaticNamespace::d + 0.1;
}

void Class::printFromSource() const
{
    std::cout<<"Class from source "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl;
}

main.cpp:

#include <iostream>
#include "Class.hpp"

int main ()
{
    Class test_class;

    test_class.printFromHeader();

#if 1
    test_class.printFromSource();
#endif

    Class::printDouble();
}

If you set the preprocessor ifs to true you will have 2 translation units, otherwise there will be just one. As you can see the different behavior of the code in the two cases is compatible to the fact that each translation unit in this example owns an independent copy of the static variable. This is just a trivial example of course...

  • It's really interesting to see this dependency on the translation units. With just one single translation unit I get the exact output I expect, eg: 1.1. With multiple translation units `printFromHeader` and the static method `printDouble` output **1** while `printFromSource` gives **1.1**. As an additional confirmation to what we are seeing it's possible to add `&StaticNamespace::d` into the couts and we'll see **different memory addresses** – helmet_23 Mar 04 '16 at 12:51
  • The above behavior holds also for the Intel compiler – helmet_23 Mar 04 '16 at 12:55
  • Together with the link to [Deprecation of the static keyword… no more?](http://stackoverflow.com/questions/4726570/deprecation-of-the-static-keyword-no-more) you provided in the other answer, I think we can close this question – helmet_23 Mar 04 '16 at 13:08