No-one knows the type of char + char

Quick quiz! Given the following:

void f(unsigned int);
void f(int);
void f(char);

Which overload gets called by the following?

char x = 1;
char y = 2;
f(x + y);


  1. f(unsigned int)
  2. f(int)
  3. f(char)
  4. No-one knows the type of char + char

If you answered 4), congratulations! And if you answered 2), maybe you tried the code on your own computer? Most people will get f(int) when they try this code, but this is actually not specified by the standard. The only thing we know for sure is that it’s not 3), f(char)!

Let’s have a look at what’s going on:

Before being passed to operator +, the operands (x and y) go through a conversion. [expr.add]§8.7¶1:

The usual arithmetic conversions are performed for operands of arithmetic or enumeration type.

What are “the usual arithmetic conversions”?


Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:
– [a bunch of rules for floats, enums etc]
– Otherwise, the integral promotions (7.6) shall be performed on both operands

So both chars go through integral promotions. Those are defined in [conv.prom]§7.6¶1:

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (7.15) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

So a char gets converted to an int if int can fit all possible values of a char. If not, they get converted to unsigned int. But any char should fit in an int, right? As it turns out, that’s not necessarily the case.

First, int could actually be the same size as char. [basic.fundamental]§6.9.1¶2:

There are five standard signed integer types : “signed char”, “short int”, “int”, “long int”, and “long long int”. In this list, each type provides at least as much storage as those preceding it in the list.

Note that it says “at least as much storage”, it doesn’t have to be more. So for instance you could have an sixteen bit system where both char and int are sixteen bits.

Second, char can be either signed or unsigned, it’s up to the implementation: [basic.fundamental]§6.9.1¶1:

It is implementation-defined whether a char object can hold negative values.

int is signed, so if char is also signed, all possible values of char will fit in an int. However, if char is unsigned, and int and char is the same size, char can actually hold larger values than int!

Let’s see an example. If char and int are both sixteen bits, int (which is always signed) can hold [-32768, 32767]. If char is signed, it can also hold [-32768, 32767], and any char fits in an int. However, if char is unsigned, it can hold [0,65535], half of which fall outside the range of int!

In the former case, chars get promoted to ints, but in the latter case, chars get promoted to unsigned ints before being summed.

So in practice, most systems will call f(int), but some might call f(unsigned int), and they would both be confirming to the standard.

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

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.


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.


In this Tweet, @willkirkby posts:


that evaluates as 1 in C/C++, but no, JavaScript is the weird language

C++ is indeed weird, or at least it’s very weakly typed. Let’s go through all the details of what’s going on here:



Starting from the right, "" is a string literal, which gets converted to a pointer, which again gets converted to a bool with the value true. This then gets passed to two operator!s, which flip it to false and back to true again. Finally, operator+ converts the bool true to the int 1.

All of this happens behind our backs, so to speak, as C++ is very eager to do conversions we didn’t explicitly ask it to do. This eagerness to do implicit conversions is by the way why you should always mark your single argument constructors and your converting operators explicit.

Detailed explanation:

Now let’s go through this in detail, quoting the C++17 standard. Starting from the right:

"" is a string literal. [lex.string]¶8:

A narrow string literal has type “array of n const char

Then comes operator!. We have an array of n const char, can we use that for operator!? [expr.unary.op]¶9 says:

The operand of the logical negation operator ! is contextually converted to bool (Clause 7); its value is true if the converted operand is false and false otherwise. The type of the result is bool.

So we need to contextually convert our array of n const char to bool. [conv]¶5 says:

Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t.

So let’s see where bool t(e); takes us when e is an array of n const char.[conv]¶2:

expressions with a given type will be implicitly converted to other types in several contexts:

  • When used as the source expression for an initialization

In our case, the expression is of type “array of n const char“, and we need a bool. We’re going to need a standard conversion sequence. [conv]¶1:

A standard conversion sequence is a sequence of standard conversions in the following order:

  • (1.1) Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
  • (1.2) Zero or one conversion from the following set: integral promotions, floating-point promotion, integral conversions, floating-point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.— […]

So we can first use an array-to-pointer conversion (1.1) to get from “array of n const char” to a pointer. We can then use a boolean conversion (1.2) to get from pointer to bool.

First, the array-to-pointer conversion, [conv.array]¶1:

An lvalue or rvalue of type “array of N T” […] can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

So we now have a pointer to the first element (the terminating \0).

And then the boolean conversion [conv.bool]¶1:

A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true.

Since our pointer is not a null pointer, its value converts to true.

Negating this twice with two operator!s is trivial, we end up back at true.

Finally, true is passed to operator+. [expr.unary.op]¶7:

The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument.

bool is not an arithmetic type, so we need to promote the bool true to an arithmetic type before passing it to opreator+. [conv.prom]¶6:

A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.

So the bool true becomes the int 1, and we’re done.

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

Why you can’t list-initialize containers of non-copyable types

Have you ever wondered why you can’t list-initialize containers of non-copyable types? This is for instance not possible:

    vector<unique_ptr<int>> vu{
        make_unique<int>(1), make_unique<int>(2)};
    //error: call to implicitly-deleted copy constructor of unique_ptr

If you ever wondered, or if you now are, read on!


Since C++11, you’re probably used to intitalizing containers like this:

    vector<int> vi1{1,2,3};
    vector<int> vi2 = {1,2,3};

This of course also works with user defined types. Let’s say you have a class Copyable, then you can for instance do:

    Copyable c1(1);
    Copyable c2(2);
    vector<Copyable> vc1{c1, c2};
    vector<Copyable> vc2 = {c1, c2};

(Copyable is just an arbitrary class which can be copied. It’s reproduced at the end of the post.)

Now what happens if we have a non-copyable class NonCopyable? (NonCopyable is just an arbitrary class which can be moved but not copied, it too is reproduced at the end of the post.)

    NonCopyable n1(1);
    NonCopyable n2(2);
    vector<NonCopyable> vn1{n1, n2}; //error: call to deleted constructor of 'const NonCopyable'
    vector<NonCopyable> vn2 = {n1, n2}; //error: call to deleted constructor of 'const NonCopyable'

Well, n1 and n2 are lvalues, so no wonder it tries to copy them. What if we turn them into rvalues, either with std::move or by creating temporaries?

    vector<NonCopyable> vn3{std::move(n1), std::move(n2)}; //error: call to deleted constructor of 'const NonCopyable'
    vector<NonCopyable> vn3{NonCopyable(4), NonCopyable(5)}; //error: call to deleted constructor of 'const NonCopyable'

So what’s going on here, why is it trying to copy our rvalues? Let’s see what the standard has to say in [dcl.init.list]¶1:

List-initialization is initialization of an object or reference from a braced-init-list.

A braced-init-list is the {element1, element2, ...} syntax we saw above. The standard continues:

Such an initializer is called an initializer list. (…) List-initialization can occur in direct-initialization or copy-initialization contexts.

So list-initialization applies both to the forms vector<Copyable> vc1{c1, c2} and vector<Copyable> vc2 = {c1, c2}, which we saw above. The former is an example of direct-initialization, the latter of copy-initialization. In both cases, {c1, c2} is the braced-init-list.

(Note that the word copy-initialization here is not what causes a copy. Copy-initialization simply refers to the form T t = expression, which doesn’t necessarily invoke the copy constructor.)

Creating the initializer_list

Now what exactly happens with the braced_init_list, and how do its elements end up inside the container we’re initializing?


An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation generated and materialized (7.4) a prvalue of type “array ofN const E“, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and thestd::initializer_list<E> object is constructed to refer to that array.

So the initializer_list can be thought of as just a wrapper for a temporary array we initialize with the elements in the braced-init-list. Sort of like if we’d been doing this:

    const Copyable arr[2] = {c1, c2};    
    vector<Copyable> vc3(initializer_list<Copyable>(arr, arr+2));

Consuming the initializer_list

Now that our initializer_list has been created and passed to the vector constructor, what can that constructor do with it? How does it get the elements out of the initializer_list and into the vector?

[initializer_list.syn] lists the very sparse interface of std::initializer_list:

constexpr const E* begin() const noexcept; // first element
constexpr const E* end() const noexcept; // one past the last element

There’s no access to the elements as rvalue references, only iterators of pointers to const, so we only get lvalues, and we need to copy. Why is there no access as rvalue references?

As we saw in the quote above, “the std::initializer_list<E> object is constructed to refer to that array.” So it only refers to it, and does not own the elements. In particular, this means that if we copy the initializer_list, we do not copy the elements, we only copy a reference to them. In fact, this is spelled out in a note [initializer_list.syn]¶1:

Copying an initializer list does not copy the underlying elements.

So even if we get passed the initializer_list by value, we do not get a copy of the elements themselves, and it would not be safe to move them out, as another copy of the initializer_list could be used again somewhere else. This is why initializer_list offers no rvalue reference access.


In summary: When you do T t{elm1, elm2}, an initializer_list is created, referring to those elements. Copying that initializer_list does not copy the elements. When a constructor takes an initializer_list, it does not know whether it’s the only consumer of those elements, so it’s not safe to move them out of the initializer_list. The only safe way to get the elements out is by copy, so a copy constructor needs to be available.

As usual, the code for this blog post is available on GitHub.

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

Appendix: The Copyable and Uncopyable classes:

class Copyable {
    Copyable(int i): i(i){}
    Copyable(const Copyable&) = default;
    Copyable(Copyable&&) = default;
    Copyable& operator=(const Copyable&) = default;
    Copyable& operator=(Copyable&&) = default;
    ~Copyable() = default;
    int i;

class NonCopyable {
    NonCopyable(int i): i(i){}
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable(NonCopyable&&) = delete;
    NonCopyable& operator=(const NonCopyable&) = default;
    NonCopyable& operator=(NonCopyable&&) = default;
    ~NonCopyable() = default;
    int i;

CppQuiz Android App now available! is an open source C++ quiz site ran by me, with contributions from the C++ community. If you’re unfamiliar with it, you can read more in its “About” section.

A few weeks ago, I was contacted by Sergey Vasilchenko. Without my knowledge, he’d built a prototype Android app for CppQuiz, and asked for my input. Naturally I was delighted, and since then we’ve been collaborating a bit on the project. I’ve been making some adjustments to the backend (basically adding some API functionality to and I’ve also done a bit of testing and review of the app. Most of the work here was by Sergey of course, who implemented the app.

The app works very similarly to the site itself, with the big advantage that it also works when you’re off-line. It downloads all the questions from on startup, and updates its database regularly. Perfect for your daily, bad reception commute!

Currently training mode is the only available mode, but Sergey is working on a quiz mode too. He’s also planning to open source the app in the near future.

So, go download the C++ Quiz App now, and give it a spin!

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter. now uses C++17 is an open source C++ quiz site ran by me, with contributions from the C++ community. If you’re unfamiliar with it, you can read more in its “About” section.

Thanks to great help from Nicolas Lesser and Simon Brand, all the questions and explanations on have now been updated from C++11 to C++17. This task would have taken me a very long time to do on my own, but thanks to Nicolas and Simon, this was all done in no more than four days!

If you want to test your knowledge of C++, try out the quiz now!

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

Help save! We’re porting to C++17.

[start helping now] is an open source C++ quiz site ran by me, with contributions from the C++ community. If you’re unfamiliar with it, you can read more in its “About” section

All the CppQuiz questions are currently targetting C++11. We need to update them for C++17. Most questions will still have the same answers, we just need to update the explanations and references to the standard. A few questions will also have different answers.

Doing this all by myself is going to take months, so I would very much appreciate some help from the community. Everyone who helps will be credited in a blog post when the porting is done.

To make porting as simple as possible, I’ve created a repository containing all the questions. There is a directory for each question, named after the question number. That directory contains the source code of the question in a .cpp file, the hint and explanation in .md files, as well as a explaining everything you need to do to port the question. There’s also an issue for each question, making it easier to track progress. The issue has the same information as the file.

As soon as we’ve updated all the questions in this repository, I’ll import them back into CppQuiz, and from then on CppQuiz will always ask about C++17.

How to help porting questions

There are two ways to contribute, listed below. I prefer the first alternative, but if that prevents you from contributing, the second is also ok.

Contributing using a fork and pull requests

  1. Fork the repo by clicking the “Fork” button at the top right.
  2. Pick the issue for a question you want to port. Add a comment that you’ll be handling that issue.
  3. Follow the instructions in the issue to port the question.
  4. Make a pull request. Porting several questions in one PR is fine.

Contributing without a fork

If you think forking and PRs is too cumbersome, or you are not able to do this for other reasons, I’d still appreciate your help:

  1. Pick the issue for a question you want to port. Add a comment that you’ll be handling that issue.
  2. Follow the instructions in the issue to port the question.
  3. Paste the updated files as comments to the issue.

Other ways to help

  • Look for questions labeled “help wanted”. It means the person responsible needs a second pair of eyes.
  • Look at pull requests, review them and comment “LGTM” if they should be merged.
  • Other ideas for help are also welcome, please get in touch (see “Questions” below).


If you have any questions, either file an issue in the repo, contact @knatten on Twitter, or email me at

[start helping now]

New job: Zivid Labs!

I know it’s not Friday, but I couldn’t wait to share this: Yesterday I signed the contract for my new job as senior developer at Zivid Labs!

While looking for a new position this time, I talked to a lot of cool companies. Zivid stuck out as the most interesting, and I’m very happy to have been accepted into their team!

Zivid have made the world’s most accurate real-time 3D color camera. In other words, the world’s coolest eyes for robots 😎. The main API is in modern, clean C++, with bindings to other languages as well. My main role will be working on the C++ parts of the API, as well as other C++ systems and frameworks. This is actually my first C++ job for over five years, so you can expect me to increase activity both on this blog and on C++ conferences, both of which I really look forward to!

Why did I choose Zivid?

  • They make a really cool product!
  • They have very high ambitions for the developer experience of their users (this is important to me, as it sets a very high bar for code quality, API quality, docs quality etc.)
  • Even though they’re a “hardware company”, they treat software as a first class citizen, and an equally important success factor as the hardware itself
  • I met some great people during my interviews there, all of which I really wanted to work with
  • They have very experienced owners, like SINTEF and Geir Førre (Chipcon, Energy Micro)
  • They recently started shipping their first product, and the first customers seem to be very impressed

Now it’s time to finish paternity leave, have a few weeks of much needed vacation, then I’ll join the team on 1 October. I’m really looking forward to joining an awesome team making an awesome product!

Trying out C++17 on macOS

Update: This article is more than a year old. Recent versions of macOS ship with much better C++17 support out of the box, so you should check whether your default Clang already supports your use case before bothering with this approach.

C++17 brings a lot of cool new features. Viva64 have a nice overview. Sadly, they can’t all be used out of the box on macOS, as the default toolchain isn’t very modern.

The problem

Apple installs their own version of llvm along with Xcode. This custom llvm also uses custom version numbers, so it isn’t always easy to know which version of mainline llvm it is based on. For instance on my macOS High Sierra, I get:

$ clang --version
Apple LLVM version 9.0.0 (clang-900.0.39.2)

This version number has nothing to with the official llvm version, which is currently at 6.0.0. All I know is that it is behind.

The solution(s)

If you want to try out some new features today, you have a few options:

  • Use std::experimental to play with certain features
  • Install a more modern llvm

Using std::experimental

Sometimes, the C++ standards committee publishes experimental C++ language and library extensions for future standardization. These go in the std::experimental namespace. If you just want to play with some new features, your toolchain might already include what you’re looking for here. However, be aware that these features as they exist in the experimental namespace are not guaranteed to be identical to how they end/ended up in the actual C++ standard! So this should just be used for toying around, not  for production code.

As an example, I wanted to try out the new optional feature. This was included in a technical specification, and has been available as std::experimental::optional for a while. Note however that its interface changed before being merged to the official standard as std::optional. The former for instance lacks the has_value member function.

Installing a more modern llvm

This is the proper solution. It’s not too hard either! First, you probably don’t want to risk breaking your current Xcode setup by installing a mainline llvm interfering with the Apple llvm used by Xcode. Luckily, homebrew avoids this problem by default, by installing llvm in another location than the Apple llvm, and not adding the new one to path.

Embedded Artistry has a good guide for this. In short, all you need to do is

$ brew install --with-toolchain llvm

This will take quite some time, and you’ll end up with mainline llvm (currently version 6.0.0) in a path you can find by running:

$ brew --prefix llvm

On my machine, I get:


I’m not going to copy paste their entire guide, so I suggest you read through it for the details of how to configure your project to use the newly installed mainline llvm instead of the apple llvm you have in path.

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