3

This is the same situation for std::is_literal_type and std::is_standard_layout.

The implementation of std::is_literal_type in libc++ is

template <class _Tp> struct _LIBCPP_TEMPLATE_VIS is_literal_type
#ifdef _LIBCPP_IS_LITERAL
    : public integral_constant<bool, _LIBCPP_IS_LITERAL(_Tp)>
#else
    : integral_constant<bool, is_scalar<typename remove_all_extents<_Tp>::type>::value ||
                              is_reference<typename remove_all_extents<_Tp>::type>::value>
#endif
    {};

There is no _LIBCPP_IS_LITERAL, so the code will be

template <typename T> struct is_literal_type : integral_constant<bool,
    is_scalar<typename remove_all_extents<T>::type>::value or
    is_reference<typename remove_all_extents<T>::type>::value> {};

I wrote a demo:

#include <iostream>

using namespace std;
struct s {
    int a;
    char b;
    long c;
};
int main(int argc, char *argv[]) {
    cout << boolalpha;
    cout << is_scalar_v<typename remove_all_extents<s>::type> << endl;
    cout << is_reference_v<typename remove_all_extents<s>::type> << endl;
}

The result is false and false. But the result of is_literal_type_v<s> is true.

Could anyone explain how std::is_literal_type works?

1
  • 1
    This doesn't address the question, but I find it helpful when modifying code to keep the original formatting until I'm sure it works; that makes it easier to ensure that there weren't inadvertent changes. Yours is correct, but it would be easier to see that if the format hadn't changed. Commented Dec 29, 2018 at 15:07

2 Answers 2

5

is_literal_type is a "magic" C++ library thing. It cannot be implemented in C++ as the language currently stands (with static reflection, it should be possible, but that's C++23 at the soonest). It is implemented by using compiler-specific intrinsic tools, not by using C++ directly. _LIBCPP_IS_LITERAL is probably a macro defined by the compiler (hence it appearing undefined) which represents that particular compiler intrinsic.

As such, you really shouldn't look too closely at the implementation of this or many of the other type traits.

I imagine the version where _LIBCPP_IS_LITERAL is not defined is for compatibility with older versions of the compiler that did not expose the necessary intrinsic. So the library implementation does the best that it can without compiler support.

Sign up to request clarification or add additional context in comments.

1 Comment

And just for completeness: the reason that the modified version gets the wrong answer is simply that it's (necessarily) incomplete.
2

To add to the previous answer by @NicolBolas: It is probably wrong that _LIBCPP_IS_LITERAL is not set. Have a look at __config.h:

#if defined(_LIBCPP_COMPILER_CLANG)

[...]

#if __has_feature(is_literal)
#define _LIBCPP_IS_LITERAL(T) __is_literal(T)
#endif

[...]

#elif defined(_LIBCPP_COMPILER_GCC)

[...]

#if _GNUC_VER >= 407
[...]
#define _LIBCPP_IS_LITERAL(T) __is_literal_type(T)
[...]
#endif

Therefore the macro will be set if you are compiling with a recent enough Clang or GCC and in either case it will use a compiler intrinsic. __is_literal for Clang or __is_literal_type for GCC.

Both intrinsics are documented:

From https://clang.llvm.org/docs/LanguageExtensions.html:

__is_literal(type): Determines whether the given type is a literal type

From https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:

__is_literal_type (type)

If type is a literal type ([basic.types]) the trait is true, else it is false. Requires: type shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.

You should not use these intrinsics directly though, because as you can see they are not portable. They are defined for the standard library developers.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.