After looking at the post about non-type template argument,I have a confusion for an example in that post,I cite the example here:
struct VariableLengthString {
const char *data_ = nullptr;
constexpr VariableLengthString(const char *p) : data_(p) {}
auto operator<=>(const VariableLengthString&) const = default;
};
template<VariableLengthString S>
int bar() {
static int i = 0;
return ++i;
}
int main() {
int x = bar<"hello">(); // ERROR
}
The post says "the relevant wording is [temp.arg.nontype]/2",So I took a look at that rule,It constrains what can be a non-type template argument.
A template-argument for a non-type template-parameter shall be a converted constant expression of the type of the template-parameter.
So,I took a look at converted constant expression
,Its definitions are here:
A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only...
What is a constant expression?These rules are here:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
(2.2) an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor.
So,Is the class type VariableLengthString
a literal class?Yes,it is.what rules can prove that are here:
A type is a literal type if it is:
1. possibly cv-qualified void; or
2. a scalar type; or
3. a reference type; or
4. an array of literal type; or
a possibly cv-qualified class type that has all of the following properties:
- it has a trivial destructor,
- it is either a closure type, an aggregate type, or has at least one constexpr constructor or constructor template (possibly inherited from a base class) that is not a copy or move constructor,
- if it is not a union, all of its non-static data members and base classes are of non-volatile literal types.
The key point is that,Are these types of sub-objects of ojbects of type VariableLengthString
all literal types? Does the class VariableLengthString
have at least one constexpr constructor?Yes they are.Because of these rules:
Arithmetic types, enumeration types, pointer types, pointer to member types ([basic.compound]), std?::?nullptr_-t, and cv-qualified versions of these types are collectively called scalar types.
So,the sub-object data_
,it's of type pointer.hence,it's scalar type,also a literal type.The bullet 3 is satisfied.Does constexpr VariableLengthString(const char *p)
a constexpr constructor?Yes,it is,Because of these rules:
The definition of a constexpr constructor shall satisfy the following requirements:
- the class shall not have any virtual base classes;
- each of the parameter types shall be a literal type;
- every non-variant non-static data member and base class subobject shall be initialized
For constexpr VariableLengthString(const char *p)
,these three rules are all be satisfied.In summary,the class VariableLengthString
is a literal type and a constexpr expression of type VariableLengthString
could be used as a non-type template argument,because it satisfies the requirement of being a converted constant expression.why the code above is ill-formed?If I miss something,please help me find out them.
See Question&Answers more detail:
os