Tips & Tricks

C++ Quiz from C++ Russia 2019

Hi,

We’ve recently visited a fantastic C++ Russia conference in Moscow. It gathered about 600 developers from Russia and the CIS, and several international speakers, with keynotes by Nico Josuttis and two days full of great talks. And we also had time for a C++ quiz!

This year, the JetBrains C++ team ran the quiz as an evening event on the first conference day. The quiz we prepared consisted of two parts. The first one was rather easy, so the speed of replies mattered. The second one required a more thoughtful approach and a detailed answer to each question. People asked us to publish the second part so here it is. Read each question and the code sample, and see if you can get the answer right!

Question 1: Does this code compile?

template<typename>
struct X {};

X() -> X<void>;

X x;

struct Y {
 static X x;
};

Answer: While line 6 is fine, line 9 should not compile. If you treat it as a definition of ‘x’, then an inline is missing. If you treat it as a declaration, then CTAD doesn’t work. However, GCC somehow manages to compile this code! See this: https://gcc.godbolt.org/z/sviRE5

Question 2: Does this code compile?

template<typename>
struct Outer {
 template<typename T>
 struct Inner {
 Inner(T);
 };

 static inline Inner i { 239 };
};

Outer<float> o;

Answer: Both answers are acceptable. GCC fails with an Internal Compiler Error. Clang doesn’t compile, because, per Richard Smith’s comment, CTAD for nested classes is poorly described in the standard. MSVC compiles fine.

Question 3: Does this code compile?

template<typename T>
struct vector {
 struct size_type { size_type(bool); };

 vector(size_type, T);
};

template<>
struct vector<bool> {
 vector(bool, bool);
};

vector v { true, false };

Answer: This code should not compile. If we take a look at the `vector(size_type, T)` constructor, we notice the compiler infers T -> bool. Then the compiler will try to find `size_type` inside the vector<bool> and will fail.

Question 4: Which line contains a compilation error?

struct { int a : 3, : 4, b : 5; } a;
auto &[x, y] = a;
auto &[p, q, r] = a;

Answer: The third line contains a compilation error. From the point of view of structured bindings, `a` has 2 fields.

Question 5: Does this code compile?

#include <type_traits>

template<typename T, typename = std::enable_if_t<sizeof(T) < sizeof(int)>>
constexpr bool less_than_int() { return true; }

template<typename T, typename = std::enable_if_t<sizeof(T) >= sizeof(int)>>
constexpr bool less_than_int() { return false; }

static_assert( less_than_int<short>());
static_assert(!less_than_int<long>());

Answer: No, it doesn’t compile. It will fail with a redefinition error, because functions that only differ in their default values for template parameters are considered equivalent.

Question 6: Does this code compile?

#include <type_traits>

template<typename T, typename = std::enable_if_t<sizeof(T) < sizeof(int)>>
constexpr bool less_than_int() { return true; }

template<typename T>
constexpr bool less_than_int() { return false; }

static_assert( less_than_int<short>());
static_assert(!less_than_int<long>());

Answer: No, it doesn’t compile. `less_than_int<short>()` leads to an ambiguous function call.

Question 7: Does this code compile?

#include <type_traits>

template<typename T, typename = std::enable_if_t<sizeof(T) > sizeof(int)>>
constexpr bool greater_than_int(int) { return true; }

template<typename T>
constexpr bool greater_than_int(...) { return false; }

static_assert(!greater_than_int<short>(0));
static_assert( greater_than_int<long>(0));

Answer: No, it doesn’t compile. The `>` inside `enable_if_t` is parsed as the end of the template arguments, not as greater sign.

Question 8: For each of the three pieces of code below, state if (by the standard) it doesn’t compile, has Undefined Behavior, or works fine.

//First piece
union {
  int x;
  int y;
} u {};

void test() {
  std::cout << u.y;
}

//Second piece
union {
  struct { int x; };
  struct { int y; };
} u {};

void test() {
  std::cout << u.y;
}

//Third piece
union {
  struct { int value; } x;
  struct { int value; } y;
} u {};

void test() {
  std::cout << u.y.value;
}

Answer: The first piece has UB as it takes an inactive union member. The second one shouldn’t compile because anonymous structs are not described in the C++ standard, even though MSVC, GCC, and Clang support them. The third piece works, as union variants form a common initial sequence.

Question 9: In C++20, the initialization of which variable (p1, p2, q1, or q2) guarantees the output order “1”, “2”?

#include <cstdio>

struct P { int x, y; };

struct Q {
  int x, y;
  Q(int x, int y) : x(x), y(y) {}
};

int one() { puts("1"); return 1; }
int two() { puts("2"); return 2; }

P p1{one(), two()};
P p2(one(), two());
Q q1{one(), two()};
Q q2(one(), two());

Answer: In lines 13 and 15, `{}` guarantees the evaluation (and thus printing) order. In line 16, the order cannot be guaranteed. Line 14 contains the most complicated case. We can’t even provide a proper reference to the standard’s text, but the discussion in the WG21 mailing list suggests the initialization of aggregates should guarantee the order (even with `()`).

Question 10: Which of the following variables has a dangling reference (per C++20)?

struct A {
  int const& v;
};

struct B {
  int const& v;
  B(int const& v) : v(v) {}
};

A const& ac = A{2 + 2};
A const& ar = A(2 + 2);
B const& bc = B{2 + 2};
B const& br = B(2 + 2);

Answer: Line 10 is fine – for the aggregator’s fields a temporary object lifetime extension works. Lines 12 and 13 are not that successful. Line 11 is actually interesting: the initialization of aggregates with `()` doesn’t give the same guarantees as with `{}`.

Question 11: What will be the program output? (Coroutines TS, С++20)

using namespace std;
using namespace std::string_literals;

template<typename Range>
generator<char> f(Range const & range) {
 for (auto const & str : range) {
 for (char c : str)
 co_yield c;
 }
}

int main() {
 for (char c : f(array { "aba"sv, "caba"sv }))
 cout << c;
}

Answer: This program contains an Undefined Behavior, because there is a temporary object passed to the coroutine with `const &`. So, after the coroutine is suspended, this object is destroyed. Therefore, from the coroutine body there is a call to an already destroyed object.

Question 12: What’s wrong with the traverse function? (Coroutines TS, С++20)

template<typename T>
struct Tree {
 T const & value() const;

 Tree const * left() const;
 Tree const * right() const;
};

template<typename T>
generator<reference_wrapper<T const>> traverse(Tree<T> const * tree) {
 if (!tree) co_return;

 for (auto t : traverse(tree->left())) co_yield t;
 co_yield cref(tree->value());
 for (auto t : traverse(tree->right())) co_yield t;
}

Answer: The traverse function works, but asymptotically is not efficient. The tree is traversed in O(n*d), where n is the number of elements and d is depth.

Question 13: Which function will be called, #1 or #2?

#include <type_traits>

template<typename T>
 requires std::is_same<T, int>()
void f(T); // #1

void f(long); // #2

void f() {
 f(0);
}

Answer: This program doesn’t compile, because the argument of the requires-clause can’t be a call-expression.

Question 14: Which function will be called, #1 or #2?

#include <type_traits>

template<typename T>
 requires (std::is_same<T, int>())
void f(T); // #1

void f(long); // #2

void f() {
 f(0);
}

Answer: This program doesn’t compile, because the argument of the requires-clause should be exactly bool, not merely convertible to bool.

Question 15: Does this code contain a redefinition error?

template<typename T> requires true void f() {}
template<typename T> void f() requires true {}

Answer: Clang compiles this code, but GCC does not (https://gcc.godbolt.org/z/tDmoEU). But it seems that Clang is right – since these functions are not equivalent, the requires-clause on the first line relates to the template heads, while the requires-clause on the second relates to the function itself. So, the template heads for them are different.

How many answers did you get right? Let us know!

Cheers,
Your ReSharper C++ Team

image description