The meaning of “user-defined type” is so obvious that the Standard doesn’t define it. But it uses the term over a dozen times, so it might be good to know what it means.
Prof. Stroustrup knows what it means and he is very clear that any type that is not built-in is user-defined. (See the second paragraph of section 9.1 in Programming Principles and Practice Using C++.) He even specifically calls out “standard library types” as an example of user-defined types. In other words, a user-defined type is any compound type.
The Standard does seem to agree in several places. For example in [dcl.type.simple] the standard says:
The other simple-type-specifiers specify either a previously-declared user-defined type or one of the fundamental types. (Emphasis mine.)
In context, it is pretty clear that std::string (for example) is a simple-type-specifier and it clearly isn’t a fundamental type so it must be a user-defined type. (In [basic.fundamental], the standard says that there are two kinds of types: fundament types and compound types. It then lists the fundamentals types–there are no Standard Library types in the list.)
So what’s the problem? The problem is that sometimes the term seems to be used in a way that implies that Standard Library types are not user-defined.
Consider this from [namespace.std]:
A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited. (Emphasis mine.)
Here the standard is saying that it is legal to create a specialization like:
namespace std { template <> void swap<my_type>(my_type&, my_type&) {…} }
This is okay because:
- swap is Standard Library template and
- my_type is a user-defined type.
The reason that the standard makes the restriction that the template type must be user-defined is because it wouldn’t do to allow users to do something like this:
namespace std { template <> void swap<int>(int&, int&) {…} }
Allowing a user to define the implementation of std::swap() could lead to nasty surprises for some users. In so many words, the Standard is saying that you only get to define the implementation of std::swap() for types that you define yourself, not for int, double, etc. But what about std::string? Does the Standard intend for users to legally do this:
namespace std { template <> void swap<string>(string&, string&) {…} }
and provide an implementation of std::swap() for std::string of the user’s choosing? This is what the Standard is saying, if we choose to interpret the meaning of user-defined types as any compound type.
There are several similar references in the Standard. References where something is only permitted in instances where at least one type is a user-defined type. Consider
- common_type in Table 57 of [meta.trans.other],
- the last requirement in the first paragraph of [unord.hash], and
- is_error_code_enum and is_error_condition_enum of [syserr].
In these and other references, it really doesn’t make sense to allow users to create specializations for Standard Library types.
My informal, not statistically significant, survey of a couple of friends of mine on the Standards Committee indicated that there are Committee members who don’t use the any-compound-type-is-a-user-defined-type definition, but instead accept (as one of them said) that:
user defined types are, broadly speaking, types that aren’t mentioned in the standard.
I think the Standard should be clear about the definition of this term and not leave it up to use to guess, because, while user-defined types are good for C++, user-defined terms are not.