4

Here is a header file containing an inline variable:

// inline.hpp
#pragma once

#include <iostream>

struct Test {
    ~Test() { std::cout << "deleted" << std::endl; }
};

inline const Test test;

...included into two .cpp files:

// usage.cpp
#include "inline.hpp"
// main.cpp
#include "inline.hpp"
auto main() -> int { return 0; }

This program prints "deleted" twice which is unexpected. I thought there was only a single instance of every inline variable, so I was expecting only one "deleted".

Is this a bug of the compiler? Or did I do something wrong?

The code is compiled with VS2017.

Holt
  • 36,600
  • 7
  • 92
  • 139
slyx
  • 2,063
  • 1
  • 19
  • 28
  • 1
    Actually, inline global variable is declared in each cpp file. So, you have two objects, not one. Here is a great [post](https://www.fluentcpp.com/2019/07/23/how-to-define-a-global-constant-in-cpp/) – NutCracker Nov 04 '19 at 14:49
  • 1
    @BoBTFish I don't see how this link is relevant... The linked is 7 years old, and this question is about a C++17 feature. – Holt Nov 04 '19 at 14:51
  • 2
    @NutCracker Your link contradicts your comment. The linker is supposed to toss out all but one definition that all of the TU's share. – NathanOliver Nov 04 '19 at 14:52
  • @Holt But it doesn't really change anything. Note I pasted it as a relevant comment, not a duplicate, but the problem (and solution) is basically the same. – BoBTFish Nov 04 '19 at 14:53
  • 3
    That looks like a compiler bug to me. – molbdnilo Nov 04 '19 at 14:53
  • May be, VS2017 has to be forced to C++17 i.e. `/std:c++17`. (Unfortunately, I forgot its default.) Otherwise, this looks to me like a compiler bug too. – Scheff's Cat Nov 04 '19 at 14:55
  • Even if there are multiple definitions, there should be only a single object (the object will have the same address in all translation units). So it's a possible compiler bug. – Some programmer dude Nov 04 '19 at 14:55
  • 1
    @BoBTFish You'd have to add a lot of explanation as to why *"it doesn't really change anything"*... For me, it changes everything. `extern int X;` is a declaration, `inline int X;` is a definition. In one case, you need a single definition in a translation unit with multiple "reference" to it via `extern` declarations, in the other cases, you have multiple inline definition, without violation of ODR (thanks to `inline`), but all definitions refering to the same object. – Holt Nov 04 '19 at 14:55
  • 1
    This has been fixed in VS 2019. – user7860670 Nov 04 '19 at 14:55

1 Answers1

5

Is this a bug of compiler?

As far as I can tell, yes. GCC and Clang, (as well as VS2019 according to comments) print "deleted" only once.

A non-volatile const inline variable in namespace scope has external linkage. The behaviour that you describe appears to imply internal linkage, which is wrong behaviour from the compiler.

For completeness, the relevant standard rules (from latest draft, emphasis added by me, bracketed emphasised parts added by me):

[basic.link]

A name having namespace scope has internal linkage if it is the name of

  • ... explicitly declared static; [does not apply] or

  • a non-template variable of non-volatile const-qualified type, [applies...] unless

    • ...
    • it is inline or exported, [exception applies] or

... A name having namespace scope that has not been given internal linkage above [applies] and that is the name of

  • a variable; [applies] or

  • ...

has its linkage determined as follows:

  • if the enclosing namespace has internal linkage, the name has internal linkage; [does not apply]

  • otherwise, if the declaration of the name is attached to a named module ... [does not apply]

  • otherwise, the name has external linkage. [applies]

eerorika
  • 232,697
  • 12
  • 197
  • 326