5

I found a peculiar thing: static member functions of a class/struct cannot be called a global scope, unless they have a return value.

This program does not compile:

struct test
{
  static void dostuff()
  {
      std::cout << "dostuff was called." << std::endl;
  }
};

test::dostuff();

int main()
{  
   return 0;
}

Giving us the following under GCC v4.8.3:

main.cpp:12:16: error: expected constructor, destructor, or type conversion before ';' token
test::dostuff();
               ^

However, by adding a return value to dostuff() and assigning it to a global variable, the program compiles and works as intended:

struct test
{
  static int dostuff()
  {
      std::cout << "dostuff was called." << std::endl;
      return 0;
  }
};

int i = test::dostuff();

int main()
{
   return 0;
}

This yields the expected output:

dostuff was called.

Can anyone explain to me why this is the case and if there is a work-around that doesn't involve creating global variables?

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    It works exactly the same for standalone functions, I do not see how question is related to static member functions. – Slava May 03 '15 at 14:48
  • 1
    If you had written it like this: `static int i = (test::dostuff(), 0);` my bet is that it would have worked, that doesn't explain it, but adds to it :) – Tommy Andersen May 03 '15 at 14:50
  • 2
    @Allan Deutsch My congratulations! You found a method how to call a function in a namespace scope!:) However I can say you in confidence how you can call a function in a namespace scope even if it does not have a return value. For example int i = test::dostuff(), 10; – Vlad from Moscow May 03 '15 at 14:52

2 Answers2

4

You cannot call functions at global scope because, from [basic.link]

A program consists of one or more translation units (Clause 2) linked together. A translation unit consists of a sequence of declarations.

test::dostuff(); is not a declaration - so you cannot have it as a standalone function call (the fact that it's a static member function is irrelevant - could be a free function, a non-static member function called on a global object, etc.). On the other hand, int i = test::dostuff(); is a declaration: of a variable i of type int at global scope. That's why it is allowed. Note that it's not the fact that dostuff() has a return value that is relevant - it's the fact that you're declaring a variable with that return value.

Barry
  • 286,269
  • 29
  • 621
  • 977
3

That's how C++ works—normally, code belongs into functions. "Global" code belongs into the main function, main, which begins execution when the program starts.

The only code potentially executed before the start of main are initialisers of global (static and namespace-scope) variables, such as the i in your example. But you shouldn't use this as "this is how to execute code at global scope." Use is as what it's intended for: "this is how to initialise a global variable."

If you need code to run at program start, put it at the beginning of main. It is quite rare you'd need code to run before that on its own, without caring for its return value. If you do find yourself in such a situation, faking initialisation is a possible workaround. But remember the caveats, such as the generally unspecified order of such before-main code execution.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • I came across this while writing code to generate type metadata before program execution. I don't care what order the metadata is generated in, as long as all of it is generated before program code runs and attempts to access it. I guess you could call this one of those rare instances? – Allan Deutsch May 03 '15 at 14:57
  • 2
    @AllanDeutsch Probably so, but mind the caveats. In particular, C++ doesn't guarantee that such initialisers will be called before `main` starts. All it guarantees is that all global variables defined in one translation unit (`.cpp` file) will be initialised before any function from that translation unit is first called, and that global vars in the same translation unit are initialised in order of appearance in source. It might still be safest to hook it to an initialisation function called explicitly. – Angew is no longer proud of SO May 03 '15 at 15:00
  • *Global variables in a single translation unit (source file) are initialized in the order in which they are defined. The order of initialization of global variables in different translation units is unspecified.* [reference](https://stackoverflow.com/a/3746249/5983841). Thank you for your answer, but the last paragraph makes me think that the order of initializations in global scope in a single translate unit is unspecified, which is not true. – Rick Dec 25 '19 at 05:36