2

Even after reading this question about non explicit inline namespace scoped variables, defined in headers, I am a bit paranoid about explicit inline namespace scope variables being ok, since AFAIK violations against the ODR are UB and not diagnosis is required. Is my understanding correct that explicitly inline specified constexpr (and const non volatile alike) defined variables at namespace scope are inline variables and therefore their ODR usage is ok when used in different translation units? Even cppreference.com is contradicting itself, when it sometimes say inline variables have to be external for the ODR usage exception, while on another page only internal linkage inline variables are ok in general, and external only with additional requirements.

Basically are these assumptions right?:

/*! @file some_header.hpp */
#ifndef HEADER_GUARD
#define HEADER_GUARD

constexpr int global_non_expl_inline = 42; //UB
static constexpr int global_non_expl_inline_static = 42; //UB
inline int global_expl_inline = 42; //ok
inline int static global_expl_inline_explicit_static = 42; //? external linkage by default but static explicit but still ok?
inline int extern global_expl_inline_explicit_extern = 42; //UB

namespace foo {
constexpr int global_non_expl_inline = 42; //UB
static constexpr int global_non_expl_inline_static = 42; //UB
inline int global_expl_inline = 42; //ok
inline int static global_expl_inline_explicit_static = 42; //? external linkage by default but static explicit but still ok?
inline int extern global_expl_inline_explicit_extern = 42; //UB
}

namespace {
inline int extern global_expl_inline_explicit_extern_but_unnamed_ns = 42; //ok
}

struct bar{
    static int const in_class_static = 42;//ok
    static int in_class_but_out_of_source_def;
};

int bar::in_class_but_out_of_source_def = 42;//UB

#endif
Jason
  • 36,170
  • 5
  • 26
  • 60
Superlokkus
  • 4,731
  • 1
  • 25
  • 57
  • `constexpr int global_non_expl_inline = 42;` why would this and other be UB? Other than `extern` these items seems to be fine. – user7860670 Sep 23 '19 at 15:36
  • *"Even cppreference.com is contradicting itself"*. which pages? – Jarod42 Sep 23 '19 at 15:36
  • I think the very question you linked explains it. They are inline variables with internal linkage, and that problem can bite you if you have an inline function which binds a reference to them called from different translation units. Can you be more specific about the nature of your question? – SergeyA Sep 23 '19 at 15:37
  • @Jarod42 https://en.cppreference.com/w/cpp/language/inline with "An inline function or inline variable (since C++17) has the following properties:" vs https://en.cppreference.com/w/cpp/language/definition " inline variable with external linkage (since C++17) ... as long as all of the following is true:" – Superlokkus Sep 23 '19 at 15:39
  • @VTT Because its a non-inline non-volatile const-qualified variable, that has internal linkage and is in violation to the ODR? Also see https://stackoverflow.com/a/46107877/3537677 – Superlokkus Sep 23 '19 at 15:42
  • @VTT cppreference.com says "A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable." – Superlokkus Sep 23 '19 at 15:43
  • @SergeyA Its about safe patterns for constant definitions in library/interface headers – Superlokkus Sep 23 '19 at 15:44
  • @Superlokkus: I don't understand what it is you find to be contradictory. "the following properties" don't really care about linkage, so "all of the following is true" isn't really relevant. Where are the two statements that cannot both be true? – Nicol Bolas Sep 23 '19 at 15:50
  • @NicolBolas The definition site only states the exception for "inline variable with external linkage" and after the points states"If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined." meaning non external linkage inline variables would still have to have exactly one definition i.e. UB by odr usage in multiple TU. While the other page inline states ALL inline variables can have more than one definition, with further req. for ones with external linkage. – Superlokkus Sep 23 '19 at 16:01
  • That there was also no answer to this >2 year old question, which tackles part of the problem, suggests to me, that there might be more potential UB patterns out there than one might think>: https://stackoverflow.com/q/42022144/3537677 – Superlokkus Sep 23 '19 at 16:10

2 Answers2

4

Well... Since you actually managed to get myself confused with your question, I thought I'd look further into it. First, we have to categorize the relevant properties of a variable: lifetime, visibility, linkage These are influenced by the keywords: static, inline, constexpr, const, extern which you use in your question.

At namespace scope in variable definition:
- static: specifies internal linkage
- inline: allows for multiple identical definitions of the same variable in different translation units and ensures they will refer to the same object (e.g have the same adresses)
- constexpr: implies const - const: defaults to external linkage
- extern: specifies external linkage

Thus,
- global_non_expl_inline: defaults to external linkage. No problem, unless another translation unit defines another such variable with external linkage.
- global_non_expl_inline_static: internal linkage. Fine, as long as you do not define other such variables anywhere.
- global_expl_inline: External linkage and inline. No problems, unless another translation unit declares another such variable without inline.
- global_expl_inline_explicit_static: Fine, a static inline variable is meaningful, if you do not want it to be available at link time, but do want the same variable in all your translation units - e.g useful for all sorts of constants.
- global_expl_inline_explicit_extern: External linkage and inline. No problems, unless another translation unit declares another such variable without inline.
- global_expl_inline_explicit_extern_but_unnamed_ns: internal linkage according to cppreference.

At class scope:
- in_class_static: external linkage. Fine, according to cppreference, but needs a declaration at namespace scope if it is odr-used.
- in_class_but_out_of_source_def: external linkage. Also fine. This is actually the standard way.

In conclusion, there is (much) less undefined behaviour than you seem to think - which is good. There are however a few things, that are valid but do not really make sense like extern in unnamed namespaces.

Regarding your comment about this question: I cannot reproduce the issue and neither could other people in the comment section of that question. There are also other plausibility issues with the question you can find in its comment section. Bear in mind, that some questions on stackoverflow are asked by people who do not exactly know which steps they take when running into problems. I would not bother too much about that particular question ;)

iolo
  • 1,090
  • 1
  • 9
  • 20
  • Where did you get "constexpr: implies const and if static is specified also implies inline" from? cppreference.com says "A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable." – Superlokkus Sep 23 '19 at 18:18
  • Together with the preceeding sentence "A constexpr specifier used in an object declaration [or non-static member function (until C++14)] implies const. A constexpr specifier used in a function [or static member variable (since C++17)] declaration implies inline." – iolo Sep 23 '19 at 18:24
  • Where did you get that 'not a namespace-scope' thing from? – iolo Sep 23 '19 at 18:25
  • Should be fixed now – iolo Sep 23 '19 at 18:31
  • Following http://eel.is/c++draft/basic.def.odr#12.3 it still seems to me only external linkage is ok. – Superlokkus Sep 23 '19 at 18:31
  • But there are no 'more than one definition' – iolo Sep 23 '19 at 18:38
  • Due to the internal linkage? – Superlokkus Sep 23 '19 at 18:40
  • I am not sure you got the use case: Of course this definitions are in a header which will be included in multiple translation units. Since you said " external linkage. Also fine. This is actually the standard way." but this leads to linker errors and also warnings with gcc 9.1.0. – Superlokkus Sep 24 '19 at 08:32
  • "const: defaults to external linkage", don't you mean internal linkage? – mateeeeeee Jul 12 '22 at 07:25
2

(Since the question is tagged C++17, I'll use the draft standard N4659 as the reference, which avoids complication due to modules.)

First, be aware that names in different translation units refer to the same entity only if they have external linkage ([basic.link]/9). Names with internal linkage always refer to different entities in different translation units, even if their definitions appear the same.

Therefore, we can put these definitions into three groups:

  1. internal linkage
    • can appear in multiple translation units (not UB); will define different variables in different TU
  2. external linkage with inline specifier
    • can appear in multiple translation units (not UB); will define the same variable in all TU
  3. external linkage without inline specifier
    • cannot appear in multiple translation units (UB: ODR violation)

(Definitions in the first group can be problematic if the variable is odr-used in another definition with external linkage, which might violate [basic.def.odr]/(6.2).)

The following definitions belong to the first group (internal linkage):

  • both of global_non_expl_inline (It's a non-inline variable of non-volatile const-qualified type, and has no previous declaration. Thus it matches [basic.link]/(3.2))
  • both of global_non_expl_inline_static ([basic.link]/(3.1))
  • both of global_expl_inline_explicit_static ([basic.link]/(3.1))
  • global_expl_inline_explicit_extern_but_unnamed_ns ([basic.link]/(4.1))

The following definitions belong to the second group (external linkage with inline specifier):

  • both of global_expl_inline
  • both of global_expl_inline_explicit_extern

The following definition belongs to the third group (external linkage without inline specifier):

  • bar::in_class_but_out_of_source_def

(bar::in_class_static is not defined, it can appear in multiple TU but can't be odr-used without a definition.)

cpplearner
  • 13,776
  • 2
  • 47
  • 72
  • Now I understand that whole https://timsong-cpp.github.io/cppwp/n4659/basic.def.odr#6.2 part much better, thanks! – Superlokkus Sep 24 '19 at 09:02