1

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.

xmh0511
  • 7,010
  • 1
  • 9
  • 36
  • "and the implicit conversion sequence contains only" The rest of that sentence sounded kind of important, since you are in fact using an implicit conversion sequence. – Nicol Bolas May 16 '20 at 15:12
  • @NicolBolas It possible a key point,However the same things also happen on example **2** in that post,why that example is well-formed?If **and the implicit conversion sequence contains only** would be a key point. – xmh0511 May 16 '20 at 15:16
  • @NicolBolas In other words,In that example ,from "char const(&)[6]" to "char const*" that is the parameter's type of FixedLengthString 's constructor,It's a "Array-to-pointer" conversion and from "char const*" to FixedLengthString,It's a user-defined conversion.So,two conversions are here.However that example is well-formed. – xmh0511 May 16 '20 at 15:33
  • 1
    That paper is out of date: the problems it describes were fixed by [P1907R1](http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1907r1.html) (although that has very little discussion). – Davis Herring May 16 '20 at 17:55
  • There is a very simple work around: https://godbolt.org/z/RTpfHu – Oliv May 16 '20 at 19:21
  • @DavisHerring You are right,but I think another answers can also be found in the old standard,that is [const.expr/5.3](https://timsong-cpp.github.io/cppwp/n4659/expr.const#5.3),so the subobject "data_" need to satisfy above rules,it violates [const.expr/5.2](https://timsong-cpp.github.io/cppwp/n4659/expr.const#5.2),so it's not a constant expression. – xmh0511 May 17 '20 at 00:48
  • @jackX: /5.2 isn’t violated: a string literal [has static storage duration](https://timsong-cpp.github.io/cppwp/n4659/lex.string#8) (and so do its elements). It *is* a constant expression, just not one that’s usable as a template argument (in either language version). – Davis Herring May 17 '20 at 01:16
  • @DavisHerring Indeed,you are right,thanks. – xmh0511 May 17 '20 at 06:33

1 Answers1

4

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 (or reference) to a literal as an NTTP. C++20 therefore doesn't allow you to smuggle a pointer (or reference) to a literal through a class member of an NTTP.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I can't find the sentence "or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof",where are them in the standard? – xmh0511 May 16 '20 at 16:31
  • @jackX: I copied that from the wrong standard. User-defined NTTPs are not part of C++17; they're part of C++20. – Nicol Bolas May 16 '20 at 16:37
  • Hi,NicolBolas,I think the answers are here [expr.const/5.3](https://timsong-cpp.github.io/cppwp/n4659/expr.const#5.3),the template argument is used as a prvalue,so 5.3 is required,that sub-object 'data_' does not satisfy the bullet 5.2,so,it's not a constant expression but a core constant expression – xmh0511 May 16 '20 at 16:54
  • 2
    @jackX: That has nothing to do with what the proposal was discussing. There's nothing about the expression that prevents it from being a constant expression; it's all about being an NTTP. That's why it's not legal. – Nicol Bolas May 16 '20 at 17:30