Slides for P3774R0
Rename std::nontype,
and make it broadly useful

Document number:
P3775R0
Date:
2025-08-12
Audience:
LEWG
Project:
ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
Reply-To:
Jan Schultke <janschultke@gmail.com>
Source:
github.com/Eisenwave/cpp-proposals/blob/master/src/nontype-slides.cow

This document has custom controls:

  • ,  ↓ : go to the next slide
  • ,  ↑ : go to previous slide

Rename std::nontype,
and make it broadly useful P3775R0
Jan Schultke, Bronek Kozicki, Tomasz Kamiński

Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 1

Introduction

Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 2

std::function_ref refresher

std::function_ref is a "function wrapper" similar to std::function, std::move_only_function, std::copyable_function, but without ownership.
std::function_ref<R(Args...) cv noex> stores:

For illustration purposes, we'll ignore cv, noex, invoke.

Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 3

std::nontype in std::function_ref

template<class F> function_ref(F*); template<class F> function_ref(F&&); template<auto f> function_ref(nontype_t<f>); template<auto f, class U> function_ref(nontype_t<f>, U&&); template<auto f, class T> function_ref(nontype_t<f>, T*); template<auto f> function_ref(nontype_t<f>) { this->bound-entity = {}; this->thunk-ptr = [](BoundEntityType, Args&&... args) -> R { return f(std::forward<Args&&>(args)...); }; }
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 4

Alternatives considered

Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 5

Ⅰ — Nullary functions

using status_t = int; constexpr status_t perform_task() { // TODO: implement, this is a placeholder return STATUS_OK; } // OK, extracts &perform_task, // r() calls perform_task std::function_ref<status_t()> r = std::cw<&perform_task>; // OK, but m() does not call perform_task; // we create a wrapper which returns pre-computed result std::move_only_function<status_t()> m = std::cw<&perform_task>;
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 6

Ⅱ — Proper constant wrappers

struct Zero { static constexpr int value = 0; constexpr operator int() const { return value; } constexpr int operator()() const { return value; } }; // OK, calls Zero::operator() std::function_ref<int()> r = std::cw<Zero{}>; // OK, but simply returns 0 std::move_only_function<int()> m = std::cw<Zero{}>;
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 7

Ⅲ — Improper constant wrappers

struct Zero { static constexpr int value = 0; constexpr operator int() const { return value; } constexpr int operator()() const { return value; } }; // OK, calls Zero::operator() std::function_ref<int()> r = std::cw<Zero{}>; // error std::move_only_function<int(int)> m = std::cw<Zero{}>;
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 8

Ⅳ — Surrogate calls

constexpr int sqr(int x) { return x * x; } // OK, extracts &sqr std::function_ref<int(int)> r = std::cw<&sqr>; // OK, surrogate function call // because of conversion to int(*)(int) std::move_only_function<int(int)> m = std::cw<&sqr>;
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 9

Ⅴ — Function objects

struct Sqr { constexpr int operator()(int x) const { return x * x; } }; inline constexpr Sqr sqr; // OK, extracts sqr std::function_ref<int(int)> r = std::cw<sqr>; // error std::move_only_function<int(int)> m = std::cw<sqr>;
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 10

Proposed solution — std::fn

// vision for C++29 constexpr int sqr(int x) { return x * x; } function_ref<int(int)> ref = fn<sqr>; // OK, same as C++26 views::transform(range, fn<sqr>); // good for inlining move_only_function<int(int)> m = fn<sqr>; // OK, optimal boost::function_ref<int(int)> b = fn<sqr>; // OK, optimal
Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 11

Discussion

Jan Schultke  |  Slides for P3774R0 — Rename std::nontype  |  LEWG Telecon 2025-08-12  |  Slide 12