static_assert in templates


Quiz time! Which of the following programs can you expect to not compile? For bonus points, which are required by the C++ standard to not compile?

Program 1

struct B{};

template <typename T>
struct A {
//Assume sizeof(B) != 4
static_assert(sizeof(T) == 4);
};

A<B> a;

Program 2

struct B{};

template <typename T>
struct A {
//Assume sizeof(B) != 4
static_assert(sizeof(T) == 4);
};

A<B>* a;

Program 3

struct B{};

template <typename T>
struct A {
//Assume sizeof(int) != 4
static_assert(sizeof(int) == 4);
};

In case you’re not familiar with static_assert, it takes a constant boolean expression, and if it evaluates to false, you get a compilation error. The most basic example is just doing static_assert(false). If you have that anywhere in your program, compilation fails. But let’s start at the beginning:

Program 1

struct B{};

template <typename T>
struct A {
//Assume sizeof(B) != 4
static_assert(sizeof(T) == 4);
};

A<B> a;

Here we have a class template struct A, which takes a type T as its single template parameter. We then assert that the size of the provided template argument is 4.

We then define a variable a of type A<B>. In order to do that, we need the complete definition of A<B>, so that specialization of A gets implicitly instantiated. In that specialization, sizeof(T) becomes sizeof(B), which is not equal to 4, and compliation fails.

Program 2

struct B{};

template <typename T>
struct A {
//Assume sizeof(B) != 4
static_assert(sizeof(T) == 4);
};

A<B>* a;

This is the exact same problem as in Program 1, except we only define a pointer to A<B>. Does this result in a implicit instantiation? Let’s have a look at [temp.inst] (§17.7.1) ¶1 in the C++17 standard:

Unless a class template specialization has been explicitly instantiated (17.7.2) or explicitly specialized (17.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.

The class template specialization A<B> has not been explicitly instantiated nor explicitly specialized, so the question is then whether it’s implicitly instantiated. We’re only declaring a pointer to it, which doesn’t require a completely-defined object type, so it’s not instantiated. The program compiles just fine.

Program 3

struct B{};

template <typename T>
struct A {
//Assume sizeof(int) != 4
static_assert(sizeof(int) == 4);
};

In this variation, we’re asserting on the size of int, rather than the size of the template argument. And given the assumption that sizeof(int) != 4, that assertion will always fail. However, we’re never actually instantiating any specialization of A whatsoever. In Program 2, not instatiating the template allowed us to ignore the static_assert. Does the same apply here? In fact, it doesn’t. Let’s have a look at [temp.inst] (§17.6) ¶8 in the C++17 standard:

The program is ill-formed, no diagnostic required, if:

[…]

a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter

The static_assert(sizeof(int) == 4) does not depend on a template parameter, so if we were to instantiate A immediately following its definition, A would always be ill-formed.

So our program is ill-formed, no diagnostic required.

Now what does that mean? ill-formed is the terminology used by the standard for a program that’s not valid C++, where compilation is required to fail with an error message. Ill-formed, no diagnostic required however means our program is not valid C++, but that the compiler isn’t required to let us know. The standard makes no guarantees on the behaviour of the program, i.e. we have undefined behaviour.

So Program 3 has undefined behaviour. In practice however, both Clang, gcc and msvc gives a compilation error in this case.

Summary

Program Because … … the standard says In practice
1 We need the class definition Compilation error Compilation error
2 We don’t need the class definition No error No error
3 The assertion doesn’t depend on T Undefined behaviour Compilation error

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s