Inline Variables for the Standard Library (original) (raw)
Document number: P0607R0
Date: 2017-02-27
Project: Programming Language C++
Audience: Library Working Group, Library Evolution Working Group
Authors: Daniel Krügler
Reply-to: Daniel Krügler
Discussion
This proposal attempts to provide combined wording for the changes requested by the NB comments FI 9 and GB 28 against C++17.
The general approach is to follow the suggested directions described by the issues:
- Required change: Use inline variables for exactly the constants that are listed by at least one of the two NB comments, which are the following ones:
- piecewise_construct
- allocator_arg
- nullopt
- [in_place, in_place_type, in_place_index]: Already part of the working paper.
- defer_lock
- try_to_lock
- adopt_lock
- bind placeholders _1, _2, ...
- ignore
- seq
- par
- par_unseq
- Optional change: In addition to the changes above introduce consequenty inlinevariables for all namespace-scope variables with internal linkage. The proper wording for this optional change depends on the resolution of core issue CWG 1713, therefore the proposed wording has an additional sub division!
The changes in regard to consequently introducing inline variables have been made optional, because this step increases the necessary draft changes significantly and that change was not _explicitly_requested by the NB comments.
Rationale
The author of this paper recommends to apply both two suggested changes (A) and (B1) inspired by the NB comments as specified below for the following reasons: Inline variables solve a long-standing problem caused by the ODR restrictions of C++. These ODR violations are typically introduced by a basically non-preventable situation where a variable with internal linkage is odr-used in a function with external linkage due to this special constraint imposed (in more precise words as summarized) by 3.2 [basic.def.odr] (6.2.1.3):
-6- There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.6), inline variable with external linkage (7.1.6), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
- (6.1) — […]
- (6.2) — in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3), except that a name can refer to
- (6.2.1) — a non-volatile const object with internal or no linkage if the object
- (6.2.1.1) — […]
- (6.2.1.2) — […]
- (6.2.1.3) — is not odr-used in any definition of D, and
- (6.2.1.4) — […]
- (6.2.2) — a reference with internal or no linkage initialized with a constant expression such that the reference refers to the same entity in all definitions of D;
- (6.3) — […]
It should be pointed out, that similar changes to constant static data members are not needed, because they have external linkage and constexpr static data members are already implicitly inline variables (7.1.5 [dcl.constexpr] p1).
Another important point to mention is, that as of the current working draft, variable template constants also have internal linkage and are therefore susceptible to the ODR violation problem unless declared as inline variables, as specified in 3.5 [basic.link] p3:
-3- A name having namespace scope (3.3.6) has internal linkage if it is the name of
- (3.1) — […]
- (3.2) — a non-inline variable of non-volatile const-qualified type that is neither explicitly declared extern nor previously declared to have external linkage; [..]
It is expected that this state will change when CWG 1713will be resolved.
Resolved Issues
If the proposed resolution would be accepted, the following library issues will be resolved:
Number | Description |
---|---|
2888 | Variables of library tag types need to be inline variables |
2889 | Mark constexpr global variables as inline |
Proposed Resolution
At some places below, additional markup of the form
[Drafting notes: whatever — _end drafting notes_]
is provided, which is not part of the normative wording, but is solely shown to provide additional information to the reader about the rationale of the concrete wording.
The below list of suggested changes is split into two halves: List (A) is an enumeration of suggested _minimalistic_wording changes and only shows changes to types explicitly listed in either of the NB comments, while list (B1) and list (B2) separately list the additional necessary changes when a full introduction of inlinevariables are considered, depending on the outcome of CWG 1713.
(B1) and (B2) are mutually exclusive and only one of them can be combined with list (A)!
The proposed wording changes refer to the Standard C++ working draft N4640.
[Drafting notes: Searching for current usages of global constant variables in the Library, the following locations are_unaffected_:
- 18.6.1 [new.syn]: nothrow [Constant with external linkage]
- 20.2.1 [utility.syn] "in-place construction": in_place, in_place_type, in_place_index[Already part of the working paper]
— _end drafting notes_]
- A. Minimalistic suggestion (only the union of those requested by the NB comments):
- Change 20.2.1 [utility.syn], header synopsis, as indicated:
inline constexpr piecewise_construct_t piecewise_construct{};
- Change 20.4.5 [pair.piecewise] before p1 as indicated:
struct piecewise_construct_t {
explicit piecewise_construct_t() = default;
};
inline constexpr piecewise_construct_t piecewise_construct{}; - Change 20.5.2 [tuple.syn], header synopsis, as indicated:
// 20.5.3.4, tuple creation functions
inline constexpr unspecified ignore; - Change 20.6.2 [optional.syn], header synopsis, as indicated:
// 20.6.4, no-value state indicator
struct nullopt_t{see below};
inline constexpr nullopt_t nullopt(unspecified); - Change 20.6.4 [optional.nullopt] as indicated:
struct nullopt_t{see below};
inline constexpr nullopt_t nullopt(unspecified); - Change 20.10.2 [memory.syn], header synopsis, as indicated:
// 20.10.6, allocator argument tag
struct allocator_arg_t { explicit allocator_arg_t() = default; };
inline constexpr allocator_arg_t allocator_arg{}; - Change 20.10.6 [allocator.tag] as indicated:
namespace std {
struct allocator_arg_t { explicit allocator_arg_t() = default; };
inline constexpr allocator_arg_t allocator_arg{};
} - Change 20.14.11.4 [func.bind.place] as indicated:
-2- Placeholders should be defined as:
inline constexpr unspecified _1{};
If they are not, they shall be declared as:
extern unspecified _1;
- Change 20.19.2 [execution.syn], header synopsis, as indicated:
// 20.19.7, execution policy objects
inline constexpr sequenced_policy seq{ unspecified };
inline constexpr parallel_policy par{ unspecified };
inline constexpr parallel_unsequenced_policy par_unseq{ unspecified }; - Change 20.19.7 [execpol.objects] as indicated:
inline constexpr execution::sequenced_policy execution::seq{ unspecified };
inline constexpr execution::parallel_policy execution::par{ unspecified };
inline constexpr execution::parallel_unsequenced_policy execution::par_unseq{ unspecified }; - Change 30.4.1 [mutex.syn], header synopsis, as indicated:
inline constexpr defer_lock_t defer_lock { };
inline constexpr try_to_lock_t try_to_lock { };
inline constexpr adopt_lock_t adopt_lock { }; - Change 30.4.4 [thread.lock] p2 as indicated:
inline constexpr defer_lock_t defer_lock { };
inline constexpr try_to_lock_t try_to_lock { };
inline constexpr adopt_lock_t adopt_lock { };
- Change 20.2.1 [utility.syn], header synopsis, as indicated:
- B1. Additional suggestion (all remaining cases excluding variable templates where inline variables would be appropriate, assumes that CWG 1713 has been resolved):
- Change 17.4.2.1.3 [enumerated.types] as indicated:
-2- The enumerated type enumerated can be written:
enum enumerated { V0, V1, V2, V3, ..... };
inline
staticconst enumerated C0 (V0);
inlinestaticconst enumerated C1 (V1);
inlinestaticconst enumerated C2 (V2);
inlinestaticconst enumerated C3 (V3);
..... - Change 17.4.2.1.4 [bitmask.types] as indicated:
-2- The bitmask type bitmask can be written:
[…]
inline constexpr bitmask C0(V0);
inline constexpr bitmask C1(V1);
inline constexpr bitmask C2(V2);
inline constexpr bitmask C3(V3); - Change 18.6.1 [new.syn], header synopsis, as indicated:
[Drafting notes: The removal of the explicit static specifier for the namespace-scope constants hardware_destructive_interference_size and hardware_constructive_interference_sizeis still required because adding inline alone would still not solve the ODR violation problem here. — _end drafting notes_]
[…]
// 18.6.5, hardware interference size
inlinestaticconstexpr size_t hardware_destructive_interference_size = implementation-defined;
inlinestaticconstexpr size_t hardware_constructive_interference_size = implementation-defined; - Change 18.6.5 [hardware.interference] as indicated:
inline constexpr size_t hardware_destructive_interference_size = implementation-defined;
-1- […]
inline constexpr size_t hardware_constructive_interference_size = implementation-defined;
-2- […]
- Change 20.2.1 [utility.syn], header synopsis, as indicated:
[_Drafting notes_: With the acceptance of CWG 1713the inline specifier is no longer required for const-qualified variable templates, therefore it is recommended to remove them from the two variable templates in_place_type and in_place_index (but not from the non-template constant in_place!) — _end drafting notes_]
templateinlineconstexpr in_place_type_t in_place_type{};
[…]
templateinlineconstexpr in_place_index_t in_place_index{}; - Change 20.7.2 [variant.syn], header synopsis, as indicated:
inline constexpr size_t variant_npos = -1;
- Change 28.5.1 [re.synopt], bitmask type syntax_option_type synopsis, as indicated:
namespace std::regex_constants {
using syntax_option_type = T1;
inline constexpr syntax_option_type icase = unspecified;
inline constexpr syntax_option_type nosubs = unspecified;
inline constexpr syntax_option_type optimize = unspecified;
inline constexpr syntax_option_type collate = unspecified;
inline constexpr syntax_option_type ECMAScript = unspecified;
inline constexpr syntax_option_type basic = unspecified;
inline constexpr syntax_option_type extended = unspecified;
inline constexpr syntax_option_type awk = unspecified;
inline constexpr syntax_option_type grep = unspecified;
inline constexpr syntax_option_type egrep = unspecified;
inline constexpr syntax_option_type multiline = unspecified;
} - Change 28.5.2 [re.matchflag], bitmask type match_flag_type synopsis, as indicated:
namespace std::regex_constants {
using match_flag_type = T2;
inline constexpr match_flag_type match_default = {};
inline constexpr match_flag_type match_not_bol = unspecified;
inline constexpr match_flag_type match_not_eol = unspecified;
inline constexpr match_flag_type match_not_bow = unspecified;
inline constexpr match_flag_type match_not_eow = unspecified;
inline constexpr match_flag_type match_any = unspecified;
inline constexpr match_flag_type match_not_null = unspecified;
inline constexpr match_flag_type match_continuous = unspecified;
inline constexpr match_flag_type match_prev_avail = unspecified;
inline constexpr match_flag_type format_default = {};
inline constexpr match_flag_type format_sed = unspecified;
inline constexpr match_flag_type format_no_copy = unspecified;
inline constexpr match_flag_type format_first_only = unspecified;
} - Change 28.5.3 [re.err], error_type synopsis, as indicated:
namespace std::regex_constants {
using error_type = T3;
inline constexpr error_type error_collate = unspecified;
inline constexpr error_type error_ctype = unspecified;
inline constexpr error_type error_escape = unspecified;
inline constexpr error_type error_backref = unspecified;
inline constexpr error_type error_brack = unspecified;
inline constexpr error_type error_paren = unspecified;
inline constexpr error_type error_brace = unspecified;
inline constexpr error_type error_badbrace = unspecified;
inline constexpr error_type error_range = unspecified;
inline constexpr error_type error_space = unspecified;
inline constexpr error_type error_badrepeat = unspecified;
inline constexpr error_type error_complexity = unspecified;
inline constexpr error_type error_stack = unspecified;
}
- Change 17.4.2.1.3 [enumerated.types] as indicated:
- B2. Additional suggestion (all remaining cases including variable templates where inline variables would be appropriate, assumes that CWG 1713 has not been resolved):
- Change 17.4.2.1.3 [enumerated.types] as indicated:
-2- The enumerated type enumerated can be written:
enum enumerated { V0, V1, V2, V3, ..... };
inline
staticconst enumerated C0 (V0);
inlinestaticconst enumerated C1 (V1);
inlinestaticconst enumerated C2 (V2);
inlinestaticconst enumerated C3 (V3);
..... - Change 17.4.2.1.4 [bitmask.types] as indicated:
-2- The bitmask type bitmask can be written:
[…]
inline constexpr bitmask C0(V0);
inline constexpr bitmask C1(V1);
inline constexpr bitmask C2(V2);
inline constexpr bitmask C3(V3); - Change 18.6.1 [new.syn], header synopsis, as indicated:
[Drafting notes: The removal of the explicit static specifier for the namespace-scope constants hardware_destructive_interference_size and hardware_constructive_interference_sizeis still required because adding inline alone would still not solve the ODR violation problem here. — _end drafting notes_]
[…]
// 18.6.5, hardware interference size
inlinestaticconstexpr size_t hardware_destructive_interference_size = implementation-defined;
inlinestaticconstexpr size_t hardware_constructive_interference_size = implementation-defined; - Change 18.6.5 [hardware.interference] as indicated:
inline constexpr size_t hardware_destructive_interference_size = implementation-defined;
-1- […]
inline constexpr size_t hardware_constructive_interference_size = implementation-defined;
-2- […]
- Change 19.5.1 [system_error.syn], header <system_error> synopsis, as indicated:
[…]
// 19.5, system error support
template inline constexpr bool is_error_code_enum_v
= is_error_code_enum::value;
template inline constexpr bool is_error_condition_enum_v
= is_error_condition_enum::value; - Change 20.5.2 [tuple.syn], header synopsis, as indicated:
[…]
// 20.5.3.6, tuple helper classes
template
inline constexpr size_t tuple_size_v = tuple_size::value; - Change 20.7.2 [variant.syn], header synopsis, as indicated:
template
inline constexpr size_t variant_size_v = variant_size::value;[…]
inline constexpr size_t variant_npos = -1;
- Change 20.10.2 [memory.syn], header synopsis, as indicated:
// 20.10.7.1, usesallocator
template <class T, class Alloc>
inline constexpr bool uses_allocator_v = uses_allocator<T, Alloc>::value; - Change 20.14.1 [functional.syn], header synopsis, as indicated:
// 20.14.11, function object binders
template
inline constexpr bool is_bind_expression_v = is_bind_expression::value;
template
inline constexpr int is_placeholder_v = is_placeholder::value; - Change 20.15.2 [meta.type.synop], header <type_traits> synopsis, as indicated:
[…]
// 20.15.4.1, primary type categories
template inline constexpr bool is_void_v
= is_void::value;
template inline constexpr bool is_null_pointer_v
= is_null_pointer::value;
template inline constexpr bool is_integral_v
= is_integral::value;
template inline constexpr bool is_floating_point_v
= is_floating_point::value;
template inline constexpr bool is_array_v
= is_array::value;
template inline constexpr bool is_pointer_v
= is_pointer::value;
template inline constexpr bool is_lvalue_reference_v
= is_lvalue_reference::value;
template inline constexpr bool is_rvalue_reference_v
= is_rvalue_reference::value;
template inline constexpr bool is_member_object_pointer_v
= is_member_object_pointer::value;
template inline constexpr bool is_member_function_pointer_v
= is_member_function_pointer::value;
template inline constexpr bool is_enum_v
= is_enum::value;
template inline constexpr bool is_union_v
= is_union::value;
template inline constexpr bool is_class_v
= is_class::value;
template inline constexpr bool is_function_v
= is_function::value;// 20.15.4.2, composite type categories
template inline constexpr bool is_reference_v
= is_reference::value;
template inline constexpr bool is_arithmetic_v
= is_arithmetic::value;
template inline constexpr bool is_fundamental_v
= is_fundamental::value;
template inline constexpr bool is_object_v
is_object::value;
template inline constexpr bool is_scalar_v
= is_scalar::value;
template inline constexpr bool is_compound_v
= is_compound::value;
template inline constexpr bool is_member_pointer_v
= is_member_pointer::value;// 20.15.4.3, type properties
template inline constexpr bool is_const_v
= is_const::value;
template inline constexpr bool is_volatile_v
= is_volatile::value;
template inline constexpr bool is_trivial_v
= is_trivial::value;
template inline constexpr bool is_trivially_copyable_v
= is_trivially_copyable::value;
template inline constexpr bool is_standard_layout_v
= is_standard_layout::value;
template inline constexpr bool is_pod_v
= is_pod::value;
template inline constexpr bool is_empty_v
= is_empty::value;
template inline constexpr bool is_polymorphic_v
= is_polymorphic::value;
template inline constexpr bool is_abstract_v
= is_abstract::value;
template inline constexpr bool is_final_v
= is_final::value;
template inline constexpr bool is_signed_v
= is_signed::value;
template inline constexpr bool is_unsigned_v
= is_unsigned::value;
template <class T, class... Args> inline constexpr bool is_constructible_v
= is_constructible<T, Args...>::value;
template inline constexpr bool is_default_constructible_v
= is_default_constructible::value;
template inline constexpr bool is_copy_constructible_v
= is_copy_constructible::value;
template inline constexpr bool is_move_constructible_v
= is_move_constructible::value;
template <class T, class U> inline constexpr bool is_assignable_v
= is_assignable<T, U>::value;
template inline constexpr bool is_copy_assignable_v
= is_copy_assignable::value;
template inline constexpr bool is_move_assignable_v
= is_move_assignable::value;
template <class T, class U> inline constexpr bool is_swappable_with_v
= is_swappable_with<T, U>::value;
template inline constexpr bool is_swappable_v
= is_swappable::value;
template inline constexpr bool is_destructible_v
= is_destructible::value;
template <class T, class... Args> inline constexpr bool is_trivially_constructible_v
= is_trivially_constructible<T, Args...>::value;
template inline constexpr bool is_trivially_default_constructible_v
= is_trivially_default_constructible::value;
template inline constexpr bool is_trivially_copy_constructible_v
= is_trivially_copy_constructible::value;
template inline constexpr bool is_trivially_move_constructible_v
= is_trivially_move_constructible::value;
template <class T, class U> inline constexpr bool is_trivially_assignable_v
= is_trivially_assignable<T, U>::value;
template inline constexpr bool is_trivially_copy_assignable_v
= is_trivially_copy_assignable::value;
template inline constexpr bool is_trivially_move_assignable_v
= is_trivially_move_assignable::value;
template inline constexpr bool is_trivially_destructible_v
= is_trivially_destructible::value;
template <class T, class... Args> inline constexpr bool is_nothrow_constructible_v
= is_nothrow_constructible<T, Args...>::value;
template inline constexpr bool is_nothrow_default_constructible_v
= is_nothrow_default_constructible::value;
template inline constexpr bool is_nothrow_copy_constructible_v
= is_nothrow_copy_constructible::value;
template inline constexpr bool is_nothrow_move_constructible_v
= is_nothrow_move_constructible::value;
template <class T, class U> inline constexpr bool is_nothrow_assignable_v
= is_nothrow_assignable<T, U>::value;
template inline constexpr bool is_nothrow_copy_assignable_v
= is_nothrow_copy_assignable::value;
template inline constexpr bool is_nothrow_move_assignable_v
= is_nothrow_move_assignable::value;
template <class T, class U> inline constexpr bool is_nothrow_swappable_with_v
= is_nothrow_swappable_with<T, U>::value;
template inline constexpr bool is_nothrow_swappable_v
= is_nothrow_swappable::value;
template inline constexpr bool is_nothrow_destructible_v
= is_nothrow_destructible::value;
template inline constexpr bool has_virtual_destructor_v
= has_virtual_destructor::value;
template inline constexpr bool has_unique_object_representations_v
= has_unique_object_representations::value;// 20.15.5, type property queries
template inline constexpr size_t alignment_of_v
= alignment_of::value;
template inline constexpr size_t rank_v
= rank::value;
template <class T, unsigned I = 0> inline constexpr size_t extent_v
= extent<T, I>::value;// 20.15.6, type relations
template <class T, class U> inline constexpr bool is_same_v
= is_same<T, U>::value;
template <class Base, class Derived> inline constexpr bool is_base_of_v
= is_base_of<Base, Derived>::value;
template <class From, class To> inline constexpr bool is_convertible_v
= is_convertible<From, To>::value;
template <class T, class R = void> inline constexpr bool is_callable_v
= is_callable<T, R>::value;
template <class T, class R = void> inline constexpr bool is_nothrow_callable_v
= is_nothrow_callable<T, R>::value;// 20.15.8, logical operator traits
template<class... B> inline constexpr bool conjunction_v = conjunction<B...>::value;
template<class... B> inline constexpr bool disjunction_v = disjunction<B...>::value;
template inline constexpr bool negation_v = negation::value; - Change 20.16.2 [ratio.syn], header synopsis, as indicated:
template <class R1, class R2>
inline constexpr bool ratio_equal_v = ratio_equal<R1, R2>::value;
template <class R1, class R2>
inline constexpr bool ratio_not_equal_v = ratio_not_equal<R1, R2>::value;
template <class R1, class R2>
inline constexpr bool ratio_less_v = ratio_less<R1, R2>::value;
template <class R1, class R2>
inline constexpr bool ratio_less_equal_v = ratio_less_equal<R1, R2>::value;
template <class R1, class R2>
inline constexpr bool ratio_greater_v = ratio_greater<R1, R2>::value;
template <class R1, class R2>
inline constexpr bool ratio_greater_equal_v = ratio_greater_equal<R1, R2>::value; - Change 20.17.2 [time.syn], header synopsis, as indicated:
// 20.17.4, customization traits
template struct treat_as_floating_point;
template struct duration_values;
template inline constexpr bool treat_as_floating_point_v
= treat_as_floating_point::value; - Change 20.19.2 [execution.syn], header synopsis, as indicated:
namespace std {
// 20.19.3, execution policy type trait
template struct is_execution_policy;
template inline constexpr bool is_execution_policy_v = is_execution_policy::value;
} - Change 28.5.1 [re.synopt], bitmask type syntax_option_type synopsis, as indicated:
namespace std::regex_constants {
using syntax_option_type = T1;
inline constexpr syntax_option_type icase = unspecified;
inline constexpr syntax_option_type nosubs = unspecified;
inline constexpr syntax_option_type optimize = unspecified;
inline constexpr syntax_option_type collate = unspecified;
inline constexpr syntax_option_type ECMAScript = unspecified;
inline constexpr syntax_option_type basic = unspecified;
inline constexpr syntax_option_type extended = unspecified;
inline constexpr syntax_option_type awk = unspecified;
inline constexpr syntax_option_type grep = unspecified;
inline constexpr syntax_option_type egrep = unspecified;
inline constexpr syntax_option_type multiline = unspecified;
} - Change 28.5.2 [re.matchflag], bitmask type match_flag_type synopsis, as indicated:
namespace std::regex_constants {
using match_flag_type = T2;
inline constexpr match_flag_type match_default = {};
inline constexpr match_flag_type match_not_bol = unspecified;
inline constexpr match_flag_type match_not_eol = unspecified;
inline constexpr match_flag_type match_not_bow = unspecified;
inline constexpr match_flag_type match_not_eow = unspecified;
inline constexpr match_flag_type match_any = unspecified;
inline constexpr match_flag_type match_not_null = unspecified;
inline constexpr match_flag_type match_continuous = unspecified;
inline constexpr match_flag_type match_prev_avail = unspecified;
inline constexpr match_flag_type format_default = {};
inline constexpr match_flag_type format_sed = unspecified;
inline constexpr match_flag_type format_no_copy = unspecified;
inline constexpr match_flag_type format_first_only = unspecified;
} - Change 28.5.3 [re.err], error_type synopsis, as indicated:
namespace std::regex_constants {
using error_type = T3;
inline constexpr error_type error_collate = unspecified;
inline constexpr error_type error_ctype = unspecified;
inline constexpr error_type error_escape = unspecified;
inline constexpr error_type error_backref = unspecified;
inline constexpr error_type error_brack = unspecified;
inline constexpr error_type error_paren = unspecified;
inline constexpr error_type error_brace = unspecified;
inline constexpr error_type error_badbrace = unspecified;
inline constexpr error_type error_range = unspecified;
inline constexpr error_type error_space = unspecified;
inline constexpr error_type error_badrepeat = unspecified;
inline constexpr error_type error_complexity = unspecified;
inline constexpr error_type error_stack = unspecified;
}
- Change 17.4.2.1.3 [enumerated.types] as indicated:
Acknowledgements
I would like to thank Richard Smith for clarifying core-language relevant matters and for his generous acceptance even of the huge editorial changes that would result when option (B2) would be voted in.