Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding user-defined string literal compiler error ".. is not a variable" [duplicate]

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:

  1. it has a trivial destructor,
  2. 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,
  3. 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:

  1. the class shall not have any virtual base classes;
  2. each of the parameter types shall be a literal type;
  3. 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.

like image 546
xmh0511 Avatar asked May 16 '20 14:05

xmh0511


1 Answers

The code is ill-formed because the standard says so:

For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

...

  • a string literal

Emphasis added. C++17 and before would not allow you to use a pointer to a literal as an NTTP. C++20 therefore doesn't allow you to smuggle a pointer to a literal through a class member of an NTTP.

like image 144
Nicol Bolas Avatar answered Nov 08 '22 11:11

Nicol Bolas