0

Just out of curiosity, I looked at how std::is_pointer is implemented and saw stuff like this (one of multiple overloads):

template <class _Ty>
_INLINE_VAR constexpr bool is_pointer_v<_Ty*> = true; 

which if I copy and paste into my code and compile says constexpr is not valid here. What is happening?

Is it possible to implement structs like std::is_pointer and std::is_reference by us? (Not that I would, just curious so please don't come at me)

MRE with msvc 2019:

template <class _Ty>
inline constexpr bool is_pointer_v<_Ty*> = true;

int main()
{
}

--

Error: error C7568: argument list missing 
after assumed function template 'is_pointer_v'
Cool_Cornflakes
  • 315
  • 2
  • 13
  • 1
    is it valid: Yes, for that implementation. Is it valid for you to code the same way: No. You shouldn't rely on implementation details or compiler built ins. – Mgetz Mar 23 '22 at 13:36
  • 3
    Please show us an [mre] of the code that fails, and tell us how it fails (copy-paste of the full and complete error output). – Some programmer dude Mar 23 '22 at 13:37
  • @Someprogrammerdude Nvm, that was the intellisense error. I updated the code with full error message. – Cool_Cornflakes Mar 23 '22 at 13:41
  • 1
    You are missing a part of your declaration. You need to have a `template inline constexpr is_pointer_v<_Ty> = false` as the default value. Else the compiler can't deduce it's a template specialization. – xryl669 Mar 23 '22 at 13:44
  • @xryl669 Just added that line above what I already had and the same error still persists. – Cool_Cornflakes Mar 23 '22 at 13:46
  • Sorry, needed to sort the syntax correctly. See my answer below. – xryl669 Mar 23 '22 at 13:51
  • `_INLINE_VAR` is almost certainly a macro, and one that is meant to be an implementation detail. Note that it starts with an underscore, which user-level stuff shouldn't do. – jjramsey Mar 23 '22 at 13:54
  • @jjramsey Yea i noticed that. It expanded to inline for me so I just put it as inline for MRE – Cool_Cornflakes Mar 23 '22 at 13:55
  • Ignoring reserved naming (as some name convention are reserved for compiler, or code inside namespace `std`), some standard library code is not valid C++ code. They might use intrinsic (mainly function/keyword to do "magic" stuff) or even code which would be pedantically UB (or implementation defined) for regular user (using `<` for `std::less`). – Jarod42 Mar 23 '22 at 13:57
  • Does not have to be. It could all be in FORTRAN, with compiler glue to map C++ to FORTRAN. Practically, though, most of it will be *standard C++*, but there are parts that are **impossible** to implement in *standard C++* — those will be implemented through non-public compiler extensions. Some functions could be implemented as *intrinsics* (another kind of extension). – Eljay Mar 23 '22 at 14:08

2 Answers2

5

To answer the question in the title:

No, standard library code does not need to adhere to the language rules for user code. Technically it doesn't even need to be implemented in C++, but could e.g. be directly integrated into the compiler.

However, practically the standard library code is always compiled by the compiler just as user code is, so it will not use any syntax constructs that the compiler would reject for user code. It will however use compiler-specific extensions and guarantees that user code should not generally rely on. And there are also some reservations made specifically to the standard library implementation in the standard.

For example, the snippet you are showing from the standard library implementation is not valid user code, because _Ty is a reserved identifier that may not be used by user code, because it starts with an underscore followed by an uppercase letter. Such identifiers are specifically reserved to the standard library implementation. For this reason alone most standard library code will not be valid user code.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • I thought that 2 underscore were reserved, but a single underscore is ok? – xryl669 Mar 23 '22 at 13:57
  • @xryl669 Starting with single underscore means reserved in global namespace. Starting with single underscore followed by uppercase letter _or_ double underscore anywhere means reserved in all contexts. – user17732522 Mar 23 '22 at 13:58
  • That raises another question, is there any compiler out there that respect this rule and errors out ? I've seen many code with member variable starting with an underscore. – xryl669 Mar 23 '22 at 14:00
  • 1
    @xryl669 Member variables starting with underscore followed by lower-case letter or digit are fine and very common in use (they are not in the global namespace. I don't know any compiler actually producing diagnostics for this. This probably would be cumbersome to implement since the compiler usually compiles the standard library code just as user code. It would need to specifically keep track of identifiers used in e.g. standard library headers included in user code. But you can easily show that the identifiers are reserved by e.g. writing `#define _Ty` followed by a standard library include. – user17732522 Mar 23 '22 at 14:04
  • 1
    @xryl669 clang-tidy has a check for it: https://clang.llvm.org/extra/clang-tidy/checks/bugprone-reserved-identifier.html – user17732522 Mar 23 '22 at 14:13
3

You are using a partial template specialization here. You need a complete declaration of the template to get that code to compile, like this:

template <class _Ty>
inline constexpr bool is_pointer_v = false;

template <class _Ty>
inline constexpr bool is_pointer_v<_Ty*> = true;

See here for example code

To answer your questions, the STL implementation requires specific C++ primitives that the compiler implements to support the required API. You can't have a constexpr version of the STL if the compiler does not implement it.

It's not possible to implement the complete STL without some compiler specifics code and the operating system's primitives. This is different from system to system (you can't use the STL implementation of linux on windows for example). It relies on undefined behavior, and many optimization which are known to be right for that specific compiler.

For example, you can't implement type punning (i.e converting from float* to int* and dereferencing) without UB in C++ (see here and here)

You have to rely on memcpy, but that can't be implemented without UB code (that is well defined if you write the compiler, BTW), since memcpy is accessing the memory likely not in the initial type that it's declared.

Yet, you can always copy & paste the STL code from your system and it'll always build correctly on your compiler.

xryl669
  • 3,376
  • 24
  • 47
  • Oh I see. Does this mean it had nothing to do with the specifics of the compiler? This is valid in all implementations isn't it? – Cool_Cornflakes Mar 23 '22 at 13:50
  • I was reading through the answer you linked and came across this interesting comment : "The wording 4.1 is completely and utterly broken and has since been rewritten. It disallowed all sorts of perfectly valid things: it disallowed custom memcpy implementations (accessing objects using unsigned char lvalues)" – Cool_Cornflakes Mar 23 '22 at 14:25
  • You should take a look at [What's the difference between "STL" and "C++ Standard Library"?](https://stackoverflow.com/questions/5205491/whats-the-difference-between-stl-and-c-standard-library) – François Andrieux Mar 23 '22 at 14:29