What does "see below" mean when used as a type or exception specification? - c++

Looking through the C++ standard (current draft http://isocpp.org/files/papers/N3690.pdf, sec 20.8.3 is one such place) and through LLVM's libc++ headers, I've found "see below" used as a type and exception specification. It seems to be used when no type exists, but it seemed strange to use a 2 word phrase for that instead of some sort of valid identifier.
Is it discussed somewhere in the standard or elsewhere? Why/how is it used?

see below is simply a place holder for one of a few possible types which are always described in the following text. For example here:
typedef see below element_type;
1
Type: Ptr::element_type if such a type exists; otherwise, T if
Ptr is a class template instantia-tion of the form SomePointer<T, Args>, where
Args is zero or more type arguments; otherwise, the specialization is ill-formed.
you may subsitute Ptr::element_type or T if SomePointer<T, Args> is valid for see below depending on context.
This form is named a syntactic category and is described in section 1.6 of the same document.

Related

Is `sizeof(T)` with an incomplete type a valid substitution-failure as per the C++ Standard?

I've seen it come up a few times on StackOverflow and elsewhere that decltype(sizeof(T)) can be used with std::void_t to SFINAE off of whether T is complete or not. This process is even documented by Raymond Chen in Microsoft's blog titled Detecting in C++ whether a type is defined with the explicit comment stating:
I’m not sure if this is technically legal, but all the compilers I tried seemed to be okay with it.
Is this behavior reliable and well-defined as per the C++ standard?
The only indication I can find in the standard is from [expr.sizeof]/1 wherein it states:
... The sizeof operator shall not be applied to an expression that has function or incomplete type, to the parenthesized name of such types, or to a glvalue that designates a bit-field ...
However it is unclear to me whether the wording "shall not be applied" would imply that this is "invalid" for the purposes of substitution as per the rules in [temp], or whether this is ill-formed.
ℹ️ Note: This question is not directed at any particular version of the standard, but it would be interesting to compare if this has changed at any point.
"Shall not be applied" means that it would normally be ill-formed. In an SFINAE context, if something would normally be ill-formed due to resulting in "an invalid type or expression", this becomes a substitution failure, as long as it is in the "immediate context" (C++20 [temp.deduct]/8) and not otherwise excluded from SFINAE (e.g. see p9 regarding lambda expressions).
There is no difference between "invalid" and "ill-formed" in this context. p8 explicitly says: "An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments." This wording has been present since C++11. However, in C++03, invalid expressions were not substitution failures. This is the famous "expression SFINAE" feature that was added in C++11, after compiler implementers were sufficiently convinced that they would be able to implement it.
There is no rule in the standard that says that sizeof expressions are an exception to the SFINAE rules, so as long as an invalid sizeof expression occurs in the immediate context, SFINAE applies.
The "immediate context" has still not been explicitly defined in the standard. An answer by Jonathan Wakely, a GCC dev, explains the intent. Eventually, someone might get around to formally defining it in the standard.
However, the case of incomplete types, the problem is that this technique is very dangerous. First, if the completeness check is performed twice in the same translation unit on the same type, the instantiation is only performed once; this implies that the second time it's checked, the result of the check will still be false, because the is_type_complete_v<T> will simply refer to the previous instantiation. Chen's post appears to simply be wrong about this: GCC, Clang, and MSVC all behave the same way. See godbolt. It's possible that the behaviour was different on an older version of MSVC.
Second, if there is cross-translation-unit variance: that is, is_type_complete_v<T> is instantiated in one translation unit and is false, and is instantiated in another translation unit and is true there, the program is ill-formed NDR. See C++20 [temp.point]/7.
For this reason, completeness checks are generally not done; instead, library implementers either say that you are allowed to pass incomplete types to their templates and they will work properly, or that you must pass a complete type but the behaviour is undefined if you violate this requirement, as it cannot be reliably checked at compile time.
One creative way around the template instantiation rules is to use a macro with __COUNTER__ to make sure that you have a fresh instantiation every time you use the type trait, and you have to define the is_type_complete_v template with internal linkage, to avoid the issue of cross-TU variance. I got this technique from this answer. Unfortunately, __COUNTER__ is not in standard C++, but this technique should work on compilers that support it.
(I looked into whether the C++20 source_location feature can replace the non-standard __COUNTER__ in this technique. I think it can't, because IS_COMPLETE may be referenced from the same line and column but within two different template instantiations that somehow both decide to check the same type, which is incomplete in one and complete in the other.)

The "(optional)" marker in cppreference.com documentation

Last week, I had a discussion with a colleague in understanding the documentation of C++ features on cppreference.com. We had a look at the documentation of the parameter packs, in particular the meaning of the (optional) marker:
(Another example can be found here.)
I thought it means that this part of the syntax is optional. Meaning I can omit this part in the syntax, but it is always required to be supported by the compiler to comply with the C++ standard. But he stated that it means that it is optional in the standard and that a compiler does not need to support this feature to comply to the standard. Which is it? Both of these explanations make sense to me.
I couldn't find any kind of explanation on the cppreference web site. I also tried to google it but always landed at std::optional...
The opt / (optional) suffix means the symbol is optional [for the C++ programmer to use; not the compiler to support]
As this question has been tagged language-lawyer, and as general when we look for a definite reference, let's move away from CppReference and into the standard.
Where CppReference uses the (optional) subscript, the standard uses opt; e.g. as in [temp.param]/1:
The syntax for template-parameters is:
template-parameter:
type-parameter
parameter-declaration
type-parameter:
type-parameter-key ...opt identifieropt
[... and so on]
[syntax]/1 describe the syntax notation [emphasis mine]:
In the syntax notation used in this document, syntactic categories are
indicated by italic type, and literal words and characters in constant
width type. Alternatives are listed on separate lines except in a few
cases where a long set of alternatives is marked by the phrase “one
of”. If the text of an alternative is too long to fit on a line, the
text is continued on subsequent lines indented from the first one.
An optional terminal or non-terminal symbol is indicated by the subscript “opt", so
{ expressionopt }
indicates an optional expression enclosed in braces.
Thus, you are correct, and your colleague is wrong. Particularly for your example of template parameter packs (which we introduce by the optional ... after typename) the identifier that follows after typename..., which names the pack (or the template parameter, if ... is omitted), is optional.
But he stated that it means that it is optional in the standard and that a compiler does not need to support this feature to comply to the standard.
The ridiculousness of this claim becomes even more clear if we annotate the "optional permutations" of a class template with a single type template parameter:
template<typename>
// ^^^^^^^^ type-parameter
// (omitting optional '...' and 'identifier')
struct S;
template<typename T>
// ^^^^^^^^^^ type-parameter
// (omitting optional '...')
struct S;
template<typename...>
// ^^^^^^^^^^^ type-parameter
// (omitting optional 'identifier')
struct S;
template<typename... Ts>
// ^^^^^^^^^^^^^^ type-parameter
struct S;
If the claim above was true, only the first of these four would need to be supported by a compliant implementation (based solely on grammar, in this contrived example), which would mean a compiler vendor could offer a compliant implementation where we could never name neither template (type) parameters nor function parameters.
It means that particular token is optional. For instance both these declarations work:
template <class... Args>
void foo();
template <class...>
void bar();
While I found a page that lists all of the marks, I was unable to find a page that specifies what the marks are intended to mean. Still, I might ask your colleague to take a look at some other pages, with the goal of the colleague abandoning the idea that "optional" means "optional to support". (This is not a definitive argument, but many would find it persuasive.) I found two good examples at Function declaration.
Function declaration:
noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional)
Focus on cv (short for "const-volatile"), which is marked "optional" and which is "only allowed in non-static member function declarations". Your colleague's interpretation of this marker would mean that compilers do not have to support const member functions, as the const keyword is "optional".
Function definition, the first option for function-body:
ctor-initializer(optional) compound-statement
The "optional" part here is the member initializer list (only allowed in constructors). Is your colleague ready to claim that a compiler need not support member initializer lists?
Sometimes one should look at the familiar to understand annotations.

Deprecated std::is_literal_type in C++17

According to cppreference, the trait std::is_literal_type is deprecated in C++17. The question is why and what is the preferred replacement for the future to check whether a type is a literal type.
As stated in P0174:
The is_literal type trait offers negligible value to generic code, as what is really needed is the ability to know that a specific construction would produce constant initialization. The core term of a literal type having at least one constexpr constructor is too weak to be used meaningfully.
Basically, what it's saying is that there's no code you can guard with is_literal_type_v and have that be sufficient to ensure that your code actually is constexpr. This isn't good enough:
template<typename T>
std::enable_if_t<std::is_literal_type_v<T>, void> SomeFunc()
{
constexpr T t{};
}
There's no guarantee that this is legal. Even if you guard it with is_default_constructible<T> that doesn't mean that it's constexpr default constructible.
What you would need is an is_constexpr_constructible trait. Which does not as of yet exist.
However, the (already implemented) trait does no harm, and allows compile-time introspection for which core-language type-categories a given template parameter might satisfy. Until the Core Working Group retire the notion of a literal type, the corresponding library trait should be preserved.
The next step towards removal (after deprecation) would be to write a paper proposing to remove the term from the core language while deprecating/removing the type trait.
So the plan is to eventually get rid of the whole definition of "literal types", replacing it with something more fine-grained.

How to interpret "terms and definitions" in the C++ standard document?

As I study different sections in the C++ standard ( Where do I find the current C or C++ standard documents? ), I would like to refer back to the "Terms and definitions", §1.3.
However, the terms and definitions are provided in a form that I cannot adequately understand or interpret, and there is no explanation given in the text regarding how to interpret them.
Consider the very first term that is defined in the "Terms and definitions" section of the standard:
1.3.1 [defns.argument]
argument
actual argument
actual parameter
<function call expression> expression in the comma-separated list
bounded by the parentheses
What does [defns.argument] refer to?
What is the meaning and purpose of the lines actual argument and actual parameter?
Does <function call expression> refer to a different "term or definition"? If so, it's not defined in the "Terms and definitions" section - why not? If not, what does it refer to? (NOTE: I am not asking what "function call expression" means, because I already know; instead, I am asking how to read and interpret the "Terms and definitions" section of the C++ standard using this simple example.)
What does [defns.argument] refer to?
[defns.argument] is the section tag; it is intended to be used for editorial purposes as it is invariant under section renumbering (e.g. in response to insertion, removal or reordering of sections). It can also be used in referring to the standard, but section numbers (relative to a published version of the standard) are more concise.
What is the meaning and purpose of the lines actual argument and actual parameter?
"actual argument" and "actual parameter" are aliases for the term "argument". You will see below under 1.3.14 [defns.parameter] that "formal argument" and "formal parameter" are aliases for the term "parameter".
The terms "actual argument" and "actual parameter" only appear in [defns.argument]; "formal argument" is described as an alias in 8.3.5p11, and "formal parameter" is used in approximately 13 places, a small fraction of the number of places where "parameter" is used.
Does <function call expression> refer to a different "term or definition"?
The angle-bracketed term is the context in which this definition applies. For example, "argument" has a different meaning in the context of a "function call expression" to in the context of a "function-like macro".
What does [defns.argument] refer to?
That's an alternative way of referring to the section (1.3.1). It should remain the same in future versions of the standard (unless it's removed), while the numbering may change.
What is the meaning and purpose of "actual argument" and "actual parameter"?
They are other terms that you might see, that mean the same thing. I believe that old versions of the spec. used "formal argument" and "actual argument" where the modern spec. uses "parameter" and "argument".
Does <function call expression> refer to a different "term or definition"?
That's the context in which "argument" has this meaning - the following sections give it different meanings in other contexts. Function call expressions are defined in 5.2.2; within such an expression, "argument" means "expression in the comma-separated list bounded by the parentheses".

Static Constant Class Members

Consider the following snippet:
struct Foo
{
static const T value = 123; //Where T is some POD-type
};
const T Foo::value; //Is this required?
In this case, does the standard require us to explicitly declare value in a translation unit? It seems I have conflicting information; boost and things like numeric_limits from the STL seem to do this sort of thing just like in my snippet.
OTOH, I remember reading somewhere (albeit a long long time ago) that you're still required to provide a declaration in a translation unit.
If this is the case, what about template specialization? Will each specialization require a declaration?
I'd appreciate your comments as to what the "right way" is.
You have to provide a definition in a translation unit too, in case you use the value variable. That means, if for example you read its value.
The important thing is that the compiler is not required to give a warning or error if you violate that rule. The Standard says "no diagnostic required" for a violation.
In the next C++ Standard version, the rule changed. A variable is not used when it is used as a constant expression. Simply reading value above where the variable is initialized directly in the class means that still no definition is required then.
See the definition of use in section 3.2 One Definition Rule of the Standard and requirement for a definition for static data-members in 9.4.2, paragraph 4 and 5 (in the C++98 Standard. Appears in paragraph 3 and 4 in the n2800 draft of the next Standard).
Correction: The rule already changed for c++03: If the variable appears where a integral constant expression is required, no definition is needed (quoting from an unofficial revisions list for the 2003 update), see resolution for this language defect report:
An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19), is the operand of the sizeof operator (5.3.3), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8)...
Note that even then, many uses are in cases where an integral constant is not required. Cases where one is, is in array dimensions or in template metaprogramming. So strictly speaking (see this report), only the c++1x solution provides really guarantee that in obvious cases also like "s == string::npos" where an integral constant is not required the definition of the static member is not needed, because the next Standard has a different, better wording of 3.2. This is however quite theoretical stuff, since most (all?) compiler don't moan anyway. Thanks for the guy in the comment section for telling me.
To add on to what litb said, from my copy of n2798:
9.4.2
[...]
2 The declaration of a static data member in its class definition is not a definition and
may be of an incomplete type other than cv-qualified void. The definition for a static
data member shall appear in a namespace scope enclosing the member’s class definition. In
the definition at namespace scope, the name of the static data member shall be qualified
by its class name using the :: operator.
You don't have to provide a definition for static integral constant members if you don't use them in some way that requires them to be stored in memory somewhere (e.g. take the address of such a member). See Stroustrup's The C++ Programming Language, section 10.4.6.2.
Edit:
Oops, I just re-read the question, and the question was for some type T. In general you would need to provide a definition, I agree. But if you used something from the int family, you wouldn't necessarily have to (with the caveat above).