简体   繁体   中英

Does an extern variable declaration also declare an (object) entity?

(All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS , with emphasis mine in the standard quotations)


Background

I'm trying to answer the question whether or not the named variable arr below

extern int arr[];  // declaration; type is int[]
int arr[10];       // definition; type is int[10]

is transferrable to that of a single entity, such that this entity has different type depending on from which context it is referred (1) , or if the entity's type is always the same, and it's just the type of the variables (whose name denotes entity) that differs depending on context.

(1) The only other case I know of where this applies is for enumerators (which are entities), which have different type depending on whether they are referred to from inside the enumeration that defines them (the type is that of the underlying type of the enumeration) or from outside of it (the type is that of the enumeration).

Question

  • Does an extern variable declaration also declare an entity (the variable name denoting the entity), such that this entity has a different type depending on whether its referred to by its extern variable declaration or its re-declaration and definition ( int[] vs int[10] above)?
  • Or, is the type of the entity always that as specified by the object definition ( int[10] above), whereas the difference in type is only due differences in the type of the variable depending on from what context we refer to it?

Details

[basic]/3 defines an entity as:

An entity is a value, object , reference, function, enumerator, type, class member, bit-field, template, template specialization, namespace, or parameter pack.

[dcl.stc]/5 covers the extern specifier, and how it can be applied to declare a variable :

The extern specifier shall be applied only to the declaration of a variable or function. [...]

[basic]/6 defines a variable as:

A variable is introduced by the declaration of a reference other than a non-static data member or of an object . The variable's name , if any, denotes the reference or object .

From here, it seems straight-forward, as per [basic]/4 and [basic]/5 , that an extern variable declaration (which must have a name) is no special case, and its name does indeed denote an entity :

/4 A name is a use of an identifier , operator-function-id , literal-operator-id , conversion-function-id , or template-id that denotes an entity or label ([stmt.goto], [stmt.label]).

/5 Every name that denotes an entity is introduced by a declaration . Every name that denotes a label is introduced either by a goto statement or a labeled-statement.

as it declares a variable whose name denotes an object (which is an entity ).

However, [intro.object]/1 says:

[...] An object is created by a definition , [...].

[...] An object has a type [...].

but the type of a variable declared using the extern specifier is not necessarily the same as that of the object which is defined elsewhere (in the re-declaration an non- extern definition of the variable):

#include <type_traits>

extern int arr[];  // extern declaration
using A = decltype(arr);

int arr[10];       // definition
using B = decltype(arr);

// All asserts below pass.
static_assert(!std::is_same_v<A, B>, "");

static_assert(std::is_same_v<A, int[]>, "");
static_assert(!std::is_same_v<A, int[10]>, "");

static_assert(std::is_same_v<B, int[10]>, "");
static_assert(!std::is_same_v<B, int[]>, "");

If the name arr as introduced by the extern variable declaration (and only that) denotes an entity , particularly that of an object, it would arguably mean that this entity has a different type depending on context/scope, which would arguably violate [intro.object]/1 regarding an object ( entity ) to have "a [single] type" .

Unfortunately, confusion between “variable” and “object” exists throughout the standard (consider that [basic.stc.auto] completely neglects the possibility of recursion), including which of those is an “entity”. However, [basic.types]/6 addresses this pretty directly:

The declared type of an array object might be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T ” and “array of N T ”) are different types.

The hedge “declared type” is used in other places like [dcl.type.auto.deduct] to talk about the type “written” for something even though the “real thing” has a different (completed or deduced) type. This is an example of another unfortunate style of description in the standard: that of specifying transformations applied to the program without making clear the order in which they are applied or which rules apply at each stage. Usually it's fairly clear what context must be applied for a rule to make sense: decltype doesn't consider a subsequent declaration that specifies an array's size and doesn't produce a type of auto .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM