Hi, 

Being able to create constexpr binding of non-trivial types seems useful for reflection and metaprogramming. For example, for querying and storing a name that is intended to be used during the runtime. This can be done (as in the TS) with const char*, but 1/ this is not very ergonomic and 2/ does this imply that every data resulting from a reflection query will be stored in the binary of the TU, regardless of whether or not it's needed? (I think it does). More generally, users might want to store dynamic arrays or arbitrary structure in a constexpr binding. For dynamic arrays, there is the ugly but fairly straightforward workaround of converting them to std::array by performing the computation twice, first for getting the size, secondly for loading the data into the std::array - ugly but doable, but we're still limited to storing trivial types. For arbitrary structures this can get quite difficult : for example how do I convert my AST structure as a bunch of arrays of trivial types? 

The current proposal (P1974) to resolve this is to introduce a new type qualifier that introduces transitive constness. 
I think it would be nice to explore other solutions : deep-constness in the type system is great, however (i suspect) it will be a lot of work to specify, and the resulting ergonomy, if I understand the proposal correctly, is not ideal - the proposal consists in introducing transitive constness only for a single branch of a type expression, thus the type for the name of the reflection API would be something like std::basic_string<propconst char>. Something that can be dealt with with aliases, but I think it would be largely preferable to not introduce such a schism between the types that can be used in a constexpr binding and types that can't. 

I would like to propose a rough draft for another solution : 
Instead of extending the type system, introduce an attribute that certifies deep-constness (let's c
all it [[deep_const]] or [[pure]]). A non-trivial class can only be used as the type of a constexpr binding if it's annotated with that attribute. 

Any class C annotated with that attribute must satisfy these requirements : 

1/ No public data members whose types are not deep-const

2/ All data members of pointers or reference type must have a deep-const form

3/ Every member function (and friend functions, and the - possibly trivial - special member functions) taking a "const& C" or "const&& C" must not dereference, pass to another function, or return, a pointer or reference subobject of C which is not in its deep-const form

4/ No mutable data member


(To make this more user friendly, I think the third constraint should not be more of an opt-in behavior, 

 where the compiler automatically insert the necessary implicit cast to fulfill it) 


A type is deep-const if it is : 

* A const trivial type 

* A const pointer, whose pointee is a deep-const type

* A const reference, whose referee is a deep-const type

* A const class/struct annotated with deep-const


The deep-const form of a type is defined by : 

* DeepConst(T)    =  const T,    defined if T is a deep-const type

* DeepConst(T&) =  const T&, defined if T is a deep-const type

* DeepConst(T*)  =  const * DeepConst(T)


This essentially amounts to just lifting `deep-constness` out of the type system and making it a trait. 

The pros are : 

* No extension to the type system, arguably easier to use and understand than propconst
* no special types or qualifiers for constexpr bindings

* the most common containers in the standard library fullfill these requirements already

The cons : 

* ? 

Curious to hear what you think and listen to other ideas in that space. 

cheers,
-JB