Mixing extern and const

2019-01-14 10:16发布

Can I mix extern and const, as extern const? If yes, does the const qualifier impose it's reign only within the scope it's declared in or should it exactly match the declaration of the translational unit it's declared in? I.e. can I declare say extern const int i; even when the actual i is not a const and vice versa?

5条回答
地球回转人心会变
2楼-- · 2019-01-14 10:43

Yes, you can use them together.

If you declare "extern const int i", then i is const over its full scope. It is impossible to redefine it as non-const. Of course you can bypass the const flag by casting it away (using const_cast).

查看更多
三岁会撩人
3楼-- · 2019-01-14 10:50

You can use them together. But you need to be consistent on your use of const because when C++ does name decoration, const is included in the type information that is used to decorate the symbol names. so extern const int i will refer to a different variable than extern int i

Unless you use extern "C" {}. C name decoration doesn't pay attention to const.

查看更多
姐就是有狂的资本
4楼-- · 2019-01-14 10:59

C++17 inline variables

If you think you want an extern const, then it is more likely that you would actually want to use C++17 inline variables.

This awesome C++17 feature allow us to:

  • conveniently use just a single memory address for each constant
  • store it as a constexpr: How to declare constexpr extern?
  • do it in a single line from one header

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

Compile and run:

g++ -o main -std=c++17 -Wall -Wextra -pedantic *.cpp
./run

GitHub upstream.

The C++ standard guarantees that the addresses will be the same. C++17 N4659 standard draft 10.1.6 "The inline specifier":

6 An inline function or variable with external linkage shall have the same address in all translation units.

cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static is not given, then it has external linkage.

Pre-C++ 17: extern const

extern const does work as in the example below, but the downsides over inline are:

  • it is not possible to make the variable constexpr with this technique, only inline allows that: How to declare constexpr extern?
  • it is less elegant as you have to declare and define the variable separately in the header and cpp file

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_i;
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

GitHub upstream.

Any way to fully inline it?

TODO: is there any way to fully inline the variable, without using any memory at all?

Much like what the preprocessor does.

This would require somehow:

  • forbidding or detecting if the address of the variable is taken
  • add that information to the ELF object files, and let LTO optimize it up

Related:

Tested in Ubuntu 18.10, GCC 8.2.0.

查看更多
我想做一个坏孩纸
5楼-- · 2019-01-14 11:03

You can use them together and you can do all sorts of things which ignore the const keyword, because that's all it is; a keyword. It tells the compiler that you won't be changing a variable which in turn allows the compiler to do some useful optomisations and stops you from changing things you didn't mean to.

Possibility.com has a decent article with some more background.

查看更多
贼婆χ
6楼-- · 2019-01-14 11:05
  • Yes, you can use them together.
  • And yes, it should exactly match the declaration in the translation unit it's actually declared in. Unless of course you are participating in the Underhanded C Programming Contest :-)

The usual pattern is:

  • file.h:
    extern const int a_global_var;
  • file.c:
    #include "file.h"
    const int a_global_var = /* some const expression */;

Edit: Incorporated legends2k's comment. Thanks.

查看更多
登录 后发表回答