文書の一覧
全部で47本あります。
- P0009R18 MDSPAN
- P1018R17 C++ Language Evolution status 🦠 pandemic edition 🦠 2022/06–2022/07
- P1083R6 Move resource_adaptor from Library TS to the C++ WP
- P1255R8 A view of 0 or 1 elements: views::maybe
- P1642R11 Freestanding Library: Easy [utilities], [ranges], and [iterators]
- P1684R3 mdarray: An Owning Multidimensional Array Analog of mdspan
- P1899R3 stride_view
- P1967R8 #embed - a simple, scannable preprocessor-based resource acquisition method
- P2047R3 An allocator-aware optional type
- P2079R3 System execution context
- P2165R4 Compatibility between tuple, pair and tuple-like objects
- P2248R5 Enabling list-initialization for algorithms
- P2295R6 Support for UTF-8 as a portable source file encoding
- P2361R5 Unevaluated strings
- P2374R4 views::cartesian_product
- P2404R3 Move-only types for equality_comparable_with, totally_ordered_with, and three_way_comparable_with
- P2417R2 A more constexpr bitset
- P2419R2 Clarify handling of encodings in localized formatting of chrono types
- P2460R2 Relax requirements on wchar_t to match existing practices
- P2474R2 views::repeat
- P2481R1 Forwarding reference to specific type/template
- P2494R2 Relaxing range adaptors to allow for move only types
- P2513R4 char8_t Compatibility and Portability Fix
- P2547R1 Language support for customisable functions
- P2548R0 copyable_function
- P2549R1 std::unexpected should have error() as member accessor
- P2561R0 operator??
- P2579R0 Mitigation strategies for P2036 “Changing scope for lambda trailing-return-type”
- P2585R1 Improving default container formatting
- P2587R1 to_string or not to_string
- P2590R2 Explicit lifetime management
- P2592R1 Hashing support for std::chrono value classes
- P2601R1 Make redundant empty angle brackets optional
- P2602R1 Poison Pills are Too Toxic
- P2609R1 Relaxing Ranges Just A Smidge
- P2610R0 2022-07 Library Evolution Polls
- P2613R1 Add the missing empty to mdspan
- P2614R0 Deprecate numeric_limits::has_denorm
- P2615R0 Meaningful exports
- P2616R0 Making std::atomic notification/wait operations usable in more situations
- P2617R0 Responses to NB comments on DTS 12907 "Extensions to C++ for Transactional Memory Version 2"
- P2618R0 C++ Standard Library Issues to be moved in Virtual Plenary, Jul. 2022
- P2620R0 Lifting artificial restriction on universal character names
- P2621R0 UB? In my Lexer?
- P2622R0 Core Language Working Group "ready" Issues for the July, 2022 meeting
- P2623R0 implicit constant initialization
- P2624R0 Make operations on bools more portable
- おわり
P0009R18 MDSPAN
多次元配列に対するstd::span
である、mdspan
の提案。
以前の記事を参照
- P0009R12 MDSPAN - WG21月次提案文書を眺める(2021年05月)
- P0009R13 MDSPAN - WG21月次提案文書を眺める(2021年10月)
- P0009R14 MDSPAN - WG21月次提案文書を眺める(2021年11月)
- P0009R15 MDSPAN - WG21月次提案文書を眺める(2022年02月)
- P0009R16 MDSPAN - WG21月次提案文書を眺める(2022年03月)
- P0009R17 MDSPAN - WG21月次提案文書を眺める(2022年07月)
このリビジョンでの変更は、LWGのフィードバックを受けての文言調整のみです。
この提案は今回(2022/07)の全体会議で承認され、C++23入りしています。
P1018R17 C++ Language Evolution status 🦠 pandemic edition 🦠 2022/06–2022/07
2022年6月から7月にかけてのEWG活動報告書。
投票にかけられた提案は以下のものです
- P2513R2 char8_t Compatibility and Portability Fixes
- P1854R3 Conversion to literal encoding should not lead to loss of meaning
そのほかにも、いくつかのコア言語IssueがCWGに転送されています。
P1083R6 Move resource_adaptor
from Library TS to the C++ WP
pmr::resource_adaptor
をLibrary Foundermental TSからワーキングドラフトへ移動する提案。
以前の記事を参照
- P1083R4 Move
resource_adaptor
from Library TS to the C++ WP - WG21月次提案文書を眺める(2022年01月) - P1083R5 Move
resource_adaptor
from Library TS to the C++ WP - WG21月次提案文書を眺める(2022年03月)
このリビジョンでの変更は
max_align_v
(最大アライメントサイズを表す定数)をinline constexpr
変数として定義aligned_raw_storage
の入れ子型::type
を削除aligned_raw_storage
はstd::aligned_storage
の完全な代替ではないことを明確化- この提案には必須ではなかったため、
aligned_object_storage
を削除 - C++26ターゲットへ変更
などです。
P1255R8 A view of 0 or 1 elements: views::maybe
std::optional
やポインタ等のmaybeモナドな対象を、その状態によって要素数0か1のシーケンスに変換するRangeアダプタviews::maybe
の提案。
以前の記事を参照
- P1255R6 : A view of 0 or 1 elements:
views::maybe
- [C++]WG21月次提案文書を眺める(2020年04月) - P1255R7 : A view of 0 or 1 elements:
views::maybe
- [C++]WG21月次提案文書を眺める(2022年05月)
このリビジョンでの変更はタイポ修正や見た目の調整のみです。
P1642R11 Freestanding Library: Easy [utilities], [ranges], and [iterators]
[utility]、<ranges>
、<iterator>
から一部のものをフリースタンディングライブラリに追加する提案。
前回の記事を参照
- P1642R3 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2020年6月)
- P1642R4 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2020年7月)
- P1642R5 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2020年12月)
- P1642R6 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2021年06月)
- P1642R7 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2021年10月)
- P1642R8 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2022年04月)
- P1642R9 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2022年05月)
- P1642R10 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2022年06月)
このリビジョンでの変更は、提案する文言の調整のみです。
この提案は今回(2022/07)の全体会議で承認され、C++23入りしています。
P1684R3 mdarray
: An Owning Multidimensional Array Analog of mdspan
多次元配列クラスmdarray
の提案。
- P1684R1 mdarray: An Owning Multidimensional Array Analog of mdspan - [C++]WG21月次提案文書を眺める(2022年03月)
- P1684R2 mdarray: An Owning Multidimensional Array Analog of mdspan - [C++]WG21月次提案文書を眺める(2022年04月)
このリビジョンでの変更は
std::mdspan
に適用された提案をこちらにも適用した- size constructible containerという要件を新設
- 整数(もしくは
range/initilizer_list
)からの構築によって、構築後のサイズを指定できるコンテナ
- 整数(もしくは
std::mdspan
からの構築のための推論補助を追加range/initilizer_list
からのコンストラクタを削除- 文言の解説を更新
などです。
P1899R3 stride_view
範囲を等間隔の要素からなる範囲に変換するRangeアダプタstride_view
の提案。
以前の記事を参照
- P1899R1
stride_view
- [C++]WG21月次提案文書を眺める(2021年11月) - P1899R2
stride_view
- [C++]WG21月次提案文書を眺める(2022年01月)
このリビジョンでの変更は、LWGのフィードバックを受けての文言の修正と、stride_view
のデフォルトコンストラクタを削除した事です。
この提案は今回(2022/07)の全体会議で承認され、C++23入りしています。
P1967R8 #embed
- a simple, scannable preprocessor-based resource acquisition method
コンパイル時(プリプロセス時)にバイナリデータをインクルードするためのプリプロセッシングディレクティブ#embed
の提案。
以前の記事を参照
- P1967R3
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2021年04月) - P1967R4
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2021年06月) - P1967R5
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2021年04月) - P1967R6
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2022年05月) - P1967R7
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2022年06月)
このリビジョンでの変更は
- C++の提案から
limit/suffix/prefix
に対する__
を削除 - 2022/6月のミーティングの議論とその反応を追記
limit(0)
を使用したif_empty
の例を追加- ファイルが空であることを検知する二つの方法(
__has_embed
とsuffix/prefix/if_empty
)の違いについての説明の追加
などです。
この提案のC言語向けの部分はC23入りしましたが、C++ではまだEWGで議論中であるためもう少し時間がかかりそうです。
P2047R3 An allocator-aware optional type
Allocator Awareなstd::optional
である、std::pmr::optional
を追加する提案。
- P2047R1 An allocator-aware optional type - [C++]WG21月次提案文書を眺める(2021年02月)
- P2047R2 An allocator-aware optional type - [C++]WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は、提案する文言の改善と、pmr::basic_optional
(アロケータの一般化)の方向性についての説明を拡充したことです。
この提案ではまだpmr::basic_optional
を含んではいませんが、LEWGのレビューではその方向性を採用することで合意されているようです。
P2079R3 System execution context
ハードウェアの提供するコア数(スレッド数)に合わせた固定サイズのスレッドプールを提供するSchedulerの提案。
- P2079R1 Parallel Executor - [C++]WG21月次提案文書を眺める(2020年8月)
- P2079R2 System execution context - [C++]WG21月次提案文書を眺める(2022年1月)
このリビジョンでの変更は
execute_all
とexecute_chunk
を削除- 処理をメインスレッドでそのまま実行することができるようにするために、コンパイル時のカスタマイズを許可するようにするように文言を調整
- (この2つの関数はそれを妨げていた)
- カスタマイズのためのアプローチと、実行コンテキストを実装定義とする範囲についての議論の追加
system_context
クラスの設計についての議論の追加- 処理の優先度に関する設計の議論を追加
などです。
P2165R4 Compatibility between tuple
, pair
and tuple-like
objects
std::pair
と2要素std::tuple
及びtuple-like
な型の間の非互換を減らし比較や代入をできるようにする提案。
前回の記事を参照
- P2165R0 Compatibility between tuple and tuple-like objects - [C++]WG21月次提案文書を眺める(2020年05月)
- P2165R1 Compatibility between tuple and tuple-like objects - [C++]WG21月次提案文書を眺める(2020年07月)
- P2165R2 Compatibility between tuple and tuple-like objects - [C++]WG21月次提案文書を眺める(2021年06月)
- P2165R3 Compatibility between tuple and tuple-like objects - [C++]WG21月次提案文書を眺める(2022年01月)
このリビジョンでの変更はLWGのレビューからのフィードバックを受けての修正がメインです。変更は1.5ページ分あるので転載はしませんが、大きな設計の変更はないはずです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2248R5 Enabling list-initialization for algorithms
値を指定するタイプの標準アルゴリズムにおいて、その際の型指定を省略できるようにする提案。
以前の記事を参照
- P2248R0 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2020年11月)
- P2248R1 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2020年12月)
- P2248R2 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2021年10月)
- P2248R3 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2021年12月)
- P2248R4 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2022年01月)
このリビジョンでの変更は
- LEWGフィードバックを受けての修正
projected_value
をprojected_value_t
にリネームprojected_value_t
とP2609ROとの関連について追記ranges::fold()
とranges::contains()
を含めた- 実装経験にHPXライブラリを追記
などです。
P2295R6 Support for UTF-8 as a portable source file encoding
C++コンパイラが少なくともUTF-8をサポートするようにする提案。
以前の記事を参照
- P2295R0 Correct UTF-8 handling during phase 1 of translation - WG21月次提案文書を眺める(2021年02月)
- P2295R3 Support for UTF-8 as a portable source file encoding - WG21月次提案文書を眺める(2021年04月)
- P2295R4 Support for UTF-8 as a portable source file encoding - WG21月次提案文書を眺める(2021年06月)
- P2295R5 Support for UTF-8 as a portable source file encoding - WG21月次提案文書を眺める(2021年07月)
このリビジョンでの変更は、提案する文言の調整のみです。
この提案は今回(2022/07)の全体会議で承認され、C++23入りしています。
P2361R5 Unevaluated strings
コンパイル時にのみ使用され、実行時まで残らない文字列リテラルについての扱いを明確化する提案。
以前の記事を参照
- P2361R0 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年04月)
- P2361R1 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年06月)
- P2361R2 Unevaluated strings - [C++]WG21月次提案文書を眺める(2021年08月)
- P2361R3 Unevaluated strings - [C++]WG21月次提案文書を眺める(2021年10月)
- P2361R4 Unevaluated strings - [C++]WG21月次提案文書を眺める(2021年11月)
このリビジョンでの変更は、asm
宣言の文法を実態に合わせたbalanced-token-seqを受け取るように変更したことなどです。
本来のasm
宣言はそのオペランドに文字列リテラルのみを取っていたためこの提案の対象となっていましたが、実際の実装では文字列リテラル以上の構文を受理するものがあったため、それを許可するようにする変更がこの提案に含まれることになりました。この変更はこの提案の内容とは直接関係なく、要するにGCCのインラインアセンブラの記法を受け入れるようにするものです。
P2374R4 views::cartesian_product
任意個数のシーケンスの直積を取って、その元のシーケンスを生成するcartesian_product_view
の提案。
以前の記事を参照
- P2374R1 views::cartesian_product - WG21月次提案文書を眺める(2021年05月)
- P2374R3 views::cartesian_product - WG21月次提案文書を眺める(2021年12月)
このリビジョンでの変更は、LWGのレビューを受けての修正です。修正項目は多岐に渡りますが、大きな設計の変更はないはずです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2404R3 Move-only types for equality_comparable_with
, totally_ordered_with
, and three_way_comparable_with
各種異種比較を定義するコンセプトのcommon_reference
要件を緩和する提案。
以前の記事を参照
- P2404R0 Relaxing equality_comparable_with's and three_way_comparable_with's common reference requirements to - WG21月次提案文書を眺める(2021年07月)
- P2404R2 Relaxing equality_comparable_with's and three_way_comparable_with's common reference requirements to - WG21月次提案文書を眺める(2022年01月)
このリビジョンでの変更は
- この提案の破壊的影響についてAnnex Cセクションを追加
- 文言の簡素化のため、3つのコンセプトの意味論要件のために新しいタイプの左辺値(lvalues denoting distinct equal objects)を導入
- 型を維持してムーブすることができる左辺値
CONVERT_TO_LVALUE<C>(E)
をCOMMON
にリネーム。- 型情報を含み省略を避けることで、よりわかりやすくした
- 説明専用コンセプトの文言の調整
- 専用の機能テストマクロの追加
などです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2417R2 A more constexpr bitset
std::bitset
をconstexpr
対応させる提案。
以前の記事を参照
- P2408R0 Ranges views as inputs to non-Ranges algorithms - WG21月次提案文書を眺める(2021年07月)
- P2408R1 Ranges views as inputs to non-Ranges algorithms - WG21月次提案文書を眺める(2021年10月)
このリビジョンでの変更は、LWGのフィードバックを受けての文言修正のみです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2419R2 Clarify handling of encodings in localized formatting of chrono types
<chrono>
のフォーマットにおいて、実行時ロケールが指定するエンコーディングとリテラルエンコーディングが異なる場合の振る舞いを規定する提案。
以前の記事を参照
- P2419R0 Clarify handling of encodings in localized formatting of chrono types - WG21月次提案文書を眺める(2021年08月)
- P2419R1 Clarify handling of encodings in localized formatting of chrono types - WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、LWGのフィードバックを受けての文言修正のみです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2460R2 Relax requirements on wchar_t to match existing practices
wchar_t
のエンコーディングについての実態になじまない制約を取り除く提案。
- P2460R0 Relax requirements on
wchar_t
to match existing practices - [C++]WG21月次提案文書を眺める(2021年10月) - P2460R1 Relax requirements on
wchar_t
to match existing practices - [C++]WG21月次提案文書を眺める(2022年05月)
このリビジョンでの変更は、提案する文言の調整のみです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2474R2 views::repeat
指定された値の繰り返しによる範囲を生成するRangeファクトリである、views::repeat
の提案。
以前の記事を参照
このリビジョンでの変更は
- タイポやフォーマットの修正
iterator{...}
をiterator(...)
に置き換え(丸かっこ初期化を使うようにした)iterator
の入れ子型reference
を削除- プライベートメンバ
W*, Bound
の初期化をコンストラクタからデフォルトメンバ初期化子に移動し、説明専用とした iterator
の_value
をnullptr
に初期化する際、デフォルト初期化するようにしたiterator
のdeference_type
を定義repeat_view
とそのイテレータのコンストラクタ、およびイテレータを変更しうる一部の操作に、bound
が空でないとう事前条件を追加repeat_view
の代わりにviews::repeat
を使用することで、repeat_view
のviews::take, views::drop
特殊化の使用を簡素化default_sentinel_t
を取るオーバーロードをnoexcept
にした
などです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2481R1 Forwarding reference to specific type/template
テンプレートパラメータ指定時にconst
と値カテゴリを推論可能にする構文の必要性について説明した文書。
このリビジョンでの変更は、Circleのアプローチについて追記した事です。
Circleとは、C++の構文を拡張してメタプログラミングのために便利な構文を追加したある種のプログラミング言語(とそのコンパイラ)です。
Circleでは、この提案の解決したい問題について次のような構文を追加しています。
template<typename T, typename... Args> void f(T&& y : std::tuple<Args...>);
これは通常のテンプレートと同様に任意の型をconst
と参照修飾まで含めて推論しつつその素の型を:
の後にある型に制限する構文です。
これはこの提案の案の1つであるQ
修飾子による指定と推論とほぼ同じことをしています。
`Q`修飾子 | Circle |
---|---|
template <typename... Ts> struct tuple { template <typename... Us, qualifiers Q> requires sizeof...(Us) == sizeof...(Ts) && (constructible_from<Ts, Q<Us>> && ...) tuple(Q<tuple<Us...>> rhs); }; |
template <typename... Ts> struct tuple { template <typename... Us, typename Rhs> requires sizeof...(Us) == sizeof...(Ts) && (constructible_from<Ts, copy_cvref_t<Rhs, Us>> && ...) tuple(Rhs&& rhs : tuple<Us...>); }; |
`Q`修飾子 | Circle |
---|---|
template <typename D> struct view_interface { template <qualifiers Q> requires forward_range<Q<D>> constexpr bool empty(this Q<D>& self) { return ranges::begin(self) == ranges::end(self); } }; |
template <typename D> struct view_interface { template <class Self> requires forward_range<Self> constexpr bool empty(this Self& self : D) { return ranges::begin(self) == ranges::end(self); } }; |
Q
修飾子と比較して、Circleのアプローチでは推論された修飾子の伝播に追加の作業が必要となりますが、それはかなり軽微です。
このアプローチの欠点は、Q
同様に構文と振る舞いが奇妙であることと、将来的にC++の宣言構文を改善しようとしたときにこれが導入されているとそれを妨げてしまう点です。
この文書はまだ提案に至っておらず、引き続き構文候補を募集しています。
P2494R2 Relaxing range adaptors to allow for move only types
何かを保持する必要があるタイプのview
型について、保持するものの型に対する要件をcopy_constructible
からmove_constructible
に弱める提案。
以前の記事を参照
- P2494R0 Relaxing range adaptors to allow for move only types - WG21月次提案文書を眺める(2021年12月)
- P2494R1 Relaxing range adaptors to allow for move only types - WG21月次提案文書を眺める(2022年01月)
このリビジョンでの変更はよくわかりません、多分文言の軽微な修正のみです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2513R4 char8_t
Compatibility and Portability Fix
char8_t
の非互換性を緩和する提案。
以前の記事を参照
- P2513R0
char8_t
Compatibility and Portability Fixes - WG21月次提案文書を眺める(2021年12月) - P2513R1
char8_t
Compatibility and Portability Fixes - WG21月次提案文書を眺める(2022年02月) - P2513R2
char8_t
Compatibility and Portability Fixes - WG21月次提案文書を眺める(2022年05月) - P2513R3
char8_t
Compatibility and Portability Fixes - WG21月次提案文書を眺める(2022年06月)
このリビジョンでの変更はよくわかりません。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています(正確にはC++20へのDR)。
P2547R1 Language support for customisable functions
カスタマイゼーションポイントの言語サポートの提案。
以前の記事を参照
このリビジョンでの変更は
virtual
と= 0;
の代わりにコンテキスト依存のキーワードcustomisable
を使用するようにした- デフォルトの実装は
customisable
関数の宣言と同時に定義できなくなった- 関数仮引数の処理とテンプレート実引数の処理は互いにかなり異なり、同居させると混乱を招くため
- オーバーライドできない関数に注釈をつけるための
final
のサポート - カスタマイズ可能な関数のオーバーロード解決を変更して、Customisable Function Prototype(CFP)自体の名前空間及び関連エンティティを常に考慮するようにした
- テンプレートな
customisable
関数とジェネリックなcustomisable
関数の例を追加 - 用語の一貫性の向上
- CPOの代わりにカスタマイズ可能な関数(customisable functions)とCFOを使用する
このリビジョンによる変更によって、以前の例は次のように変更されます
namespace std::execution { // execution::connect CFPの宣言 template<sender S, receiver R> operation_state auto connect(S s, R r) customisable; }
namespace std::ranges { // std::ranges::contains CFPの宣言 template<input_range R, typename Value> requires equality_comparable_with<range_reference_t<R>, Value> bool contains(R range, Value v) customisable; // std::ranges::contains CFPのデフォルト実装 template<input_range R, typename Value> requires equality_comparable_with<range_reference_t<R>, Value> bool contains(R&& range, const Value& v) default { for (const auto& x : range) { if (x == v) return true; } return false; } }
namespace std { template<class Key, class Compare, class Allocator> class set { // ... private: // std::ranges::contains CFPのHidden friendsによるカスタマイズ template<typename V> requires requires(const set& s, const V& v) { s.contains(v); } friend bool ranges::contains(const set& s, const V& v) override { return s.contains(v); } }; }
namespace std { template<class Key, class Hash, class KeyEq, class Allocator> class unordered_set { ... }; // std::ranges::contains CFPのクラス定義外でのカスタマイズ template<class Key, class Hash, class Eq, class Allocator, class Value> requires(const unordered_set<Key,Hash,Eq, Allocator>& s, const Value& v) { s.contains(v); } bool ranges::contains(const unordered_set<Key,Hash,Eq,Allocator>& s, const Value& v) override { return s.contains(v); } }
final
の使用例
namespace std { // 型指定get() template<typename T, typename Obj> auto get(Obj&& obj) customisable; // (1) // インデックス指定get() template<size_t N, typename Obj> auto get(Obj&& obj) customisable; // (2) // インデックスを引数によって指定する非テンプレートのget() template<size_t N, typename Obj> requires (Obj&& obj) { get<N>(std::forward<Obj>(obj)); } auto get(Obj&& obj, std::integral_constant<size_t, N>) final -> decltype(auto) { return get<N>(std::forward<Obj>(obj)); } } struct my_tuple { int x; float y; // (1)をカスタマイズ friend int& std::get<int>(my_tuple& self) noexcept override { return self.x; } friend float& std::get<float>(my_tuple& self) noexcept override { return self.y; } // (2)をカスタマイズ friend int& std::get<0>(my_tuple& self) noexcept override { return self.x; } friend float& std::get<1>(my_tuple& self) noexcept override { return self.y; } };
上記例の使用例
void example() { my_tuple t = {42, 0.0f}; int& x1 = std::get<0>(t); float& y1 = std::get<1>(t); int& x2 = std::get<int>(t); float& y2 = std::get<float>(t); int& x3 = std::get(t, std::integral_constant<std::size_t, 0>{}); float& y3 = std::get(t, std::integral_constant<std::size_t, 1>{}); }
CFOの明示的な引数の推論例(上記のget
を使用している)
template<typename T, std::size_t N> struct array { T data[N]; // 関数テンプレートの仮引数からCFOの明示的なテンプレート引数(インデックスN)を推定する template<std::size_t Idx> requires (Idx < N) friend T& std::get<Idx>(array& self) noexcept override { return self.data[Idx]; } }; template<typename First, typename Second> struct pair { First first; Second second; // クラステンプレートの仮引数からCFOの明示的なテンプレート引数(型T)を推定する friend First& std::get<First>(pair& self) noexcept override requires (!std::same_as<First, Second>) { return self.first; } // ... };
P2548R0 copyable_function
std::move_only_function
に対して、コピー可能なCallableラッパであるcopyable_function
の提案。
C++23で導入されたstd::move_only_function
は、その関数シグネチャにconst
/参照修飾とnoexcept
を指定することができ、呼び出し時に自信のconst
性と値カテゴリを保持するCallableオブジェクトまで伝播させたうえで呼び出しを行うことができます。これによって、std::move_only_function
オブジェクトのconst
有無と右辺値であるかの状態と、保持するCallbaleオブジェクトの呼び出し環境を一致させることができます。
一方std::function
にはそのようなサポートはなく、そのためにconst
修飾のミスマッチバグ等の設計上の問題がいくつか指摘されていました。
const
修飾を正しく扱えない- ムーブのみ可能な(コピーできない)Callableオブジェクトを保持できない
- 左辺値から呼び出すCallableオブジェクトしか保持できない(参照修飾を正しく扱えない)
std::move_only_function
はstd::fucntion
の持つこれらの問題と軽微ないくつかの問題(RTTIへの依存、呼び出し時の空チェック)を解決するために導入されましたが、名前が示すとおりにstd::move_only_function
のオブジェクトはムーブしかできません(ムーブしかできないCallableだけを保持可能なわけではありません)。
コピー可能なstd::move_only_function
が欲しい場合はstd::function
を使用するしかないのですが、std::function
には上記のような問題があります。また、後方互換性の保護のためにstd::function
をstd::move_only_function
のような設計に変更することもできません。
この提案は、コピー可能かつ現在のstd::function
の問題を解決した、std::move_only_function
のコピー可能なバージョンであるstd::copyable_function
を標準ライブラリに追加する提案です。
std::move_only_function
が保持するCallableはcopyable
であっても単にmovable
でしかなくても大丈夫ですが、std::copyable_function
はcopyable
なCallableしか保持できません。それ以外のところでは、std::move_only_function
にコピーコンストラクタとコピー代入演算子を追加しただけです。
現在 | この提案 |
---|---|
auto lambda{[&]() /*const*/ { … }}; function<void(void)> func{lambda}; // ✔ const auto & ref{func}; func(); // ✔ ref(); // ✔ |
auto lambda{[&]() /*const*/ { … }}; copyable_function<void(void)> func0{lambda}; // ✔ const auto & ref0{func0}; func0(); // ✔ ref0(); // ❌ operator() is NOT const! copyable_function<void(void) const> func1{lambda}; // ✔ const auto & ref1{func1}; func1(); // ✔ ref1(); // ✔ operator() is const! |
現在 | この提案 |
---|---|
auto lambda{[&]() mutable { … }}; function<void(void)> func{lambda}; // ✔ const auto & ref{func}; func(); // ✔ ref(); // ⁉✔ operator() is const! // this is the infamous constness-bug |
auto lambda{[&]() mutable { … }}; copyable_function<void(void)> func{lambda}; // ✔ const auto & ref{func}; func(); // ✔ ref(); // ❌ operator() is NOT const! copyable_function<void(void) const> tmp{lambda}; // ❌ |
P2549R1 std::unexpected
should have error()
as member accessor
std::unexpected
のエラー値取得関数をerror()
という名前にする提案。
以前の記事を参照
このリビジョンでの変更は、ベースとなるstd::expected
提案及びワーキングドラフトの更新、LEWGでの投票結果の追記、寄せられたフィードバックの反映、などです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2561R0 operator??
std::expected
などを返す関数において、エラーの伝播を自動化させる演算子??
の提案。
例外を投げうる関数を扱う関数が自身も例外を投げうる場合、例外を伝播させるための構文的なコストはゼロです。
auto foo(int i) noexcept(false) -> int; // might throw an E auto bar(int i) noexcept(false) -> int; // might throw an E auto strcat(int i) noexcept(false) -> std::string { int f = foo(i); int b = bar(i); return std::format("{}{}", f, b); } // あるいはインライン化してこう書ける auto strcat(int i) noexcept(false) -> std::string { return std::format("{}{}", foo(i), bar(i)); }
例外をハンドルせずに伝播させるために追加で何かを書く必要はありません。
ただし、例外には多くの問題があるためあまり好まれず、その代替手段の一つとしてC++23からはstd::expected<T, E>
が使用できます。
auto foo(int i) -> std::expected<int, E>; auto bar(int i) -> std::expected<int, E>; auto strcat(int i) -> std::expected<std::string, E> { auto f = foo(i); if (not f) { return std::unexpected(f.error()); } auto b = bar(i); if (not b) { return std::unexpected(b.error()); } return std::format("{}{}", *f, *b); }
こちらの場合、エラーをハンドルせず伝播させる場合でも、そのためのかなり冗長なコードを追加しなければならず、値の取り出しにおいても*
を使用しなければなりません。また、その際に考慮すべきことがいくつも潜んでいます(適切なムーブなど)。
そのため、std::expected
に似た機能を提供するライブラリでは、このような処理をマクロによってラップする機能を提供しています。
auto strcat(int i) -> std::expected<std::string, E> { SOMETHING_TRY(int f, foo(i)); SOMETHING_TRY(int b, bar(i)); return std::format("{}{}", f, b); }
この場合は例外を使用するコードにかなり近くなりますが、マクロを使用していることからf, b
の宣言を省いてインライン化することができません。これもまたマクロを工夫することで解決できますが、それは特定のコンパイラ拡張に頼っていたり適切にムーブされないなど効率的とは言えないものです。
コルーチンを用いて近しいシンタックスシュガーを再現することもできます。
auto strcat(int i) -> std::expected<std::string, E> { int f = co_await foo(i); int b = co_await bar(i); co_return std::format("{}{}", f, b); // ... or co_return std::format("{}{}", co_await foo(i), co_await bar(i)); }
しかし、現在のところコルーチンは動的なメモリ確保を必ずしも回避できないため、これもまた効率的なコードではありません。
結局のところ、現在のC++におけるstd::expected
のエラー伝播手法としては、マクロによるものがベストとなります。
別の言語、例えばRustでは、std::expected
に対応するresult
型がエラー伝播によく使用されています。Rustでは、先程のサンプルコードは例えば次のように書けます。
Rust | C++ |
---|---|
fn strcat(i: i32) -> Result<String, E> { let f = match foo(i) { Ok(i) => i, Err(e) => return Err(e), }; let b = match bar(i) { Ok(i) => i, Err(e) => return Err(e), } Ok(format!("{}{}", f, b)) } |
auto strcat(int i) -> std::expected<std::string, E> { auto f = foo(i); if (not f) { return std::unexpected(f.error()); } auto b = bar(i); if (not b) { return std::unexpected(b.error()); } return std::format("{}{}", *f, *b); } |
パターンマッチングの利用によって中間変数が必要ないなど、これだけでもRustの方が良い書き方ができますが、Rustにおいてのベストな書き方はこれではありません。
Rust | C++(例外) |
---|---|
fn strcat(i: i32) -> Result<String, E> { let f = foo(i)?; let b = bar(i)?; Ok(format!("{}{}", f, b)) // ... or simply ... Ok(format!("{}{}", foo(i)?, bar(i)?)) } |
auto strcat(int i) -> std::string { int f = foo(i); int b = bar(i); return std::format("{}{}", f, b); // ... or simply ... return std::format("{}{}", foo(i), bar(i)); } |
この場合、1文字(?
)の構文上のオーバーヘッドによって、C++の例外を用いたコードとほぼ同等の半自動エラー伝播処理を記述できています。1文字とはいえオーバーヘッドではありますが、std::expected
を使用するコードにおけるマクロに比べたらこのオーバーヘッドは無視できるでしょう。
理想的にはこれをC++に導入したいのですが、条件演算子?:
と曖昧になる可能性があるためこの?
を単項後置演算子として単純に導入できません。
// ?:と?がある場合、次のコードは auto res = a ? * b ? * c : d; // 以下の2つのパース先がある auto res1 = a ? (*(b?) * c) : d; auto res2 = ((a?) * b) ? (*c) : d;
そのため、この提案では1文字増やした??
演算子をstd::expected
等のためのエラー伝播半自動化構文として導入することを提案しています。
この演算子は上で示したRustの?
に対応するもので、コンパイル時には範囲for
のように展開されます。
展開前 | 展開後 |
---|---|
auto strcat(int i) -> std::expected<std::string, E>{ int f = foo(i)??; int b = bar(i)??; return std::format("{}{}", f, b); } |
auto strcat(int i) -> std::expected<std::string, E> { using _Return = std::try_traits< std::expected<std::string, E>>; auto&& __f = foo(i); using _TraitsF = std::try_traits< std::remove_cvref_t<decltype(__f)>>; if (not _TraitsF::is_ok(__f)) { return _Return::from_error( _TraitsF::extract_error(FWD(__f))); } int f = _TraitsF::extract_value(FWD(__f)); auto&& __b = bar(i); using _TraitsB = std::try_traits< std::remove_cvref_t<decltype(__b)>>; if (not _TraitsB::is_ok(__b)) { return _Return::from_error( _TraitsB::extract_error(FWD(__b))); } int b = _TraitsB::extract_value(FWD(__b)); return std::format("{}{}", f, b); } |
展開に当たっては、対象のオブジェクトからエラー状態と中身の値を取り出す必要があり、また、それらの値から戻り値をどう構築するかを指定する必要があります。それを担っているのがstd::try_traits
という型特性で、次の静的メンバ関数を持っています
is_ok
: オブジェクトのエラー状態を取得するextract_value/extract_error
: 正常値/エラー値を取得するfrom_value/from_error
: 正常値/エラー値からその型のオブジェクトを構築する
これは、std::expected
のような型に対して簡単にアダプトできます。
// std::optionalでの例 template <class T> struct try_traits<optional<T>> { using value_type = T; using error_type = nullopt_t; auto is_ok(optional<T> const& o) -> bool { return o.has_value(); } // extractors auto extract_value(auto&& o) -> auto&& { return *FWD(o); } auto extract_error(auto&&) -> error_type { return nullopt; } // factories auto from_value(auto&& v) -> optional<T> { return optional<T>(in_place, FWD(v)); } auto from_error(nullopt_t) -> optional<T> { return {}; } }; // std::expectedでの例 template <class T, class E> struct try_traits<expected<T, E>> { using value_type = T; using error_type = E; auto is_ok(expected<T, E> const& e) -> bool { return e.has_value(); } // extractors auto extract_value(auto&& e) -> auto&& { return *FWD(e); } auto extract_error(auto&& e) -> auto&& { return FWD(e).error(); } // factories auto from_value(auto&& v) -> expected<T, E> { return expected<T, E>(in_place, FWD(v)); } auto from_error(auto&& e) -> expected<T, E> { return expected<T, E>(unexpect, FWD(e)); } };
また、この提案のtry_traits
はC#等のnull条件演算子?.
のような演算子のために必要なものをすべて提供します。
auto f(int) -> std::expected<std::string, E>; // 将来の可能性? auto x = f(42)?.size();
P2579R0 Mitigation strategies for P2036 “Changing scope for lambda trailing-return-type”
P2036R3による後方非互換性を緩和する提案。
P2036R3(ラムダ式の後置戻り値型がキャプチャする変数のスコープの変更)はC++23のWDに導入されており、以前のバージョンに対するDRとして採択されています。P2036については以前の記事を参照。
この提案の検討段階では、この変更によって影響を受けるコードはほぼ無いだろうと思われていました。しかし、clangで実装されたところclangそのもの(llvm/libstdc++)のコードを壊している事が判明しました。それは次のようなコードです
// なんかイテレータ範囲のendの値 auto local_end = ...; [local_end](decltype(local_end) it) { return it != local_end; }; // ^^^^^^^^^^^^^^^^^^^
後置戻り値型指定ではなく引数型でキャプチャした変数を参照しているコードが存在しており、P2036R3ではこれはill-formedとしています。なぜなら、ここではまだmutable
が見えていないため、decltype((x))
の型を正しく求める事ができないためです。しかし、以前はこのx
は外の変数をキャプチャしていたため問題にならず、少なくともコードを書いた人間の意図通りには動いていました。
このようなコードはCWG2569として報告され、これを先行実装することで解決されました。ここでは、mutable
が現れる(場所に到達する)前にキャプチャ変数がdecltype
などで使用される場合、decltype(x)
は許可するもののdecltype((x))
は許可しないようにすることでmutable
の影響を受けないようにしつつ既存のコードが壊れないようにしています。
しかしその後、次のような別のコードが壊れている事が報告されました
template <typename It, typename MapFn> auto MapJoin(It first, It last, MapFn map_fn) { return std::accumulate(first, last, map_fn(*first), // a new diagnostic: error: captured variable 'first' cannot appear here [=](typename std::result_of<MapFn(decltype(*first))>::type result) { }); } void foo() { int x = [x](int y[sizeof x]) { return sizeof x; }(0); }
これらのコード破壊が報告された結果clangではP2036の実装を一旦停止したためこれ以上の破損例を収集できませんでしたが、実装された場合にはより多くのコードを壊すであろう事が予想されます。結果として、clangの実装者(筆者の方)はP2036R3は実装不可能であると考えているようです。
これらのコードに共通することは、C++11時点でジェネリックラムダが導入されていなかったことによる代替手段であることのようです。従って、現在ジェネリックラムダを使用するコードをC++11で書こうとした場合にP2036に違反するコードになる可能があり、コーナーケースであると切って捨てられるほどおかしなコードであるわけではありません。
この提案は、P2036R3の変更を修正してその悪影響を緩和しようとするものです。ここでは、5つのソリューションが提示されています。
- CWG2569の修正
mutable
が現れる前は、ラムダの外側の変数をキャプチャするmutable
が現れた場合、ラムダ式の引数宣言内でキャプチャを参照するコードをill-formedにする- パース時に
mutable
キーワードを先読みする - キャプチャされた変数を参照するものの、常に
mutable
有無を考慮しない
EWGでは5番目の解決策が選択され、この提案はそのための文言を含んでいます。
それぞれのデメリットの概要は次のようになっています
- CWG2569の修正
- ラムダの変数宣言部で一部の用法(
decltype(x)
など)だけを許可するようにする - 許可されるかが式に左右されるため理解しづらい(
decltype(*x)
はngなど) - 前述の通り、これだけでは破損するコードがまだある
- ラムダの変数宣言部で一部の用法(
mutable
が現れる前は、ラムダの外側の変数をキャプチャするmutable
が現れた場合、ラムダ式の引数宣言内でキャプチャを参照するコードをill-formedにする- 破損は減少するが完全ではない
- 後から見つかる
mutable
によってエラー有無が変わるのは奇妙
- パース時に
mutable
キーワードを先読みする- 完璧では無いものの、最善の解決策
- 実装の負担が大きい
- 後から見つかる
mutable
によって構文の意味が変わるのは奇妙
- キャプチャされた変数を参照するものの、常に
mutable
有無を考慮しない-
decltype((x))
の振る舞いが関数引数部分の終端の前後で変化する可能性がある
-
なお、この場合、キャプチャされた変数x
を参照するdecltype((x))
はx
がconst
でない限り非const
となります。すなわち、デフォルトでmutable
があるかのように扱います。
void f() { float x, &r = x; [=](decltype((x)) y) { decltype((x)) z = x; }; // ok、yの型はfloat&, zの型はconst float& [=] { []<decltype(x) P>; // ok [](decltype((x)) y){}; // ok、yの型はfloat const&(囲むラムダがコピーキャプチャしたxを参照している) [x=1](decltype((x)) y){ decltype((x)) z = x; }; // ok, yの型はint&, zの型はconst int& }; }
この提案は、CWG Issueと関連する提案でもあることからすでにCWGのレビューをパスしており、今回(2022/07)の全体会議で承認され、C++23入りしています。。
- P2598R0 “Changing scope for lambda trailing-return-type” (P2036) should not be a DR - WG21月次提案文書を眺める(2022年06月)
- CWG Issue 2569. Use of
decltype(capture)
in a lambda's parameter-declaration-clause - P2579 進行状況
P2585R1 Improving default container formatting
std::format
のコンテナに対するフォーマットを改善する提案。
以前の記事を参照
このリビジョンでの変更は、range_format_kind
をrange_format
にリネームしたことなどです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2587R1 to_string
or not to_string
std::to_string
の浮動小数点数出力を修正する提案。
以前の記事を参照
このリビジョンでの変更は、Annex Cセクションを追加したこと、機能テストマクロを追加したこと、to_wstring
にも同じ変更を適用するようにしたこと、などです。
P2590R2 Explicit lifetime management
メモリ領域上にあるトリビアルな型のオブジェクトの生存期間を開始させるライブラリ機能の提案。
以前の記事を参照
- P2590R0 Explicit lifetime management - WG21月次提案文書を眺める(2022年05月)
- P2590R1 Explicit lifetime management - WG21月次提案文書を眺める(2022年06月)
このリビジョンでの変更は、const/const volatile
オーバーロードを追加したこと、noexcept
を付加したこと、アライメントに関する事前条件を追加したこと、提案する文言の調整や修正、などです。
この提案は、今回(2022/07)の全体会議で承認され、C++23入りしています。
P2592R1 Hashing support for std::chrono value classes
<chrono>
の時間や日付を表す型に対してハッシュサポートを追加する提案。
以前の記事を参照
このリビジョンでの変更は、LWGからのフィードバックの反映、カレンダー型の特殊化について構築のされ方によっては未規定の値を取る可能性がある事を追記、Heterogeneous Overloadのサポート(していない)についての追記、などです。
この提案はC++26を目指して現在LEWGでレビュー中です。
P2601R1 Make redundant empty angle brackets optional
クラステンプレート使用時に、不要な<>
を省略可能にする提案。
以前の記事を参照
このリビジョンでの変更は
- クラステンプレートと変数テンプレートの両方を対象とすることを明確にした
- デフォルトテンプレート引数と推定された引数の区別を明確にするために説明と文言を修正
- 空の
<>
を省略しても意味がない例とその有無が重要な例を追記
などです。
このリビジョンで追加された例
template <class T = int> struct C { C(T); }; std::vector<C> v ; // C<int> struct S { C c; }; // C<int> struct D : C {}; // C<int> void foo(C c); // C<int> C x = 1.0; // C<double> C<> y = 1.0; // C<int>, intへの暗黙変換
P2602R1 Poison Pills are Too Toxic
標準ライブラリから、Poison Pillと呼ばれるオーバーロードを削除する提案。
以前の記事を参照
このリビジョンでの変更は、機能テストマクロを追加したこと、比較系のCPO(<compare>
にあるstrong_order
など)を含めたことです。
この変更はどうやらC++20へのDRとされそうです(まだなってない)。
P2609R1 Relaxing Ranges Just A Smidge
射影(プロジェクション)を取るアルゴリズムについて、その制約を緩和する提案。
以前の記事を参照
このリビジョンでの変更は、indirect_value_t
を説明専用にしたこと、P2248R4への影響について追記したこと、__cpp_lib_ranges
の値を上げるようにしたこと、などです。
P2610R0 2022-07 Library Evolution Polls
2022年の5月に予定されている、LEWGでの全体投票の予定表。
次の提案が、LWGに進むための投票にかけられます。
- C++23
- P0429R9 flat_map
- P1222R4 flat_set
- P0792R10 function_ref
- P2505R4 Monadic Functions For expected
- P2585R0 Improving Default Container Formatting
- P2446R2 views::as_rvalue
- P2278R4 cbegin Should Always Return A Constant Iterator
- P2248R5 Enabling List-Initialization For Algorithms
- P2539R1 Should The Output Of print To A Terminal Be Synchronized With The Underlying Stream?
- P2510R3 Formatting Pointers
- P2551R2 Clarify Intent Of Individually Specializable Numeric Traits
- P2599R2 index_type & size_type In mdspan
- P2604R0 mdspan: Rename pointer, data, And contiguous
- P2613R1 Add The Missing empty To mdspan
- C++26
P2613R1 Add the missing empty
to mdspan
std::mdspan
にempty()
メンバ関数を追加する提案。
以前の記事を参照
このリビジョンでの変更は、LWGのフィードバックを受けての文言調整のみです。
この提案は今回(2022/07)の全体会議で承認され、C++23入りしています。
P2614R0 Deprecate numeric_limits::has_denorm
std::numeric_limits::has_denorm
関連の定数を非推奨化する提案。
std::numeric_limits::has_denorm
はその環境で、浮動小数点数型T
が非正規化数をサポートしているかを調べるものです。これはコンパイル時定数であり、浮動小数点数型T
非正規化数をサポートしている/いない/わからない、をコンパイル時に取得するものです。
IEE754準拠の浮動小数点数型であっても、ハードウェアによっては非正規化数をサポートしていない場合がありその場合はソフトウェアエミュレーションによってサポートされている場合があります。この場合、同じ系統のハードウェアであっても将来のバージョンでサポートされる可能性があり、この時にABI破壊を回避しようと思うとstd::denorm_indeterminate
を常に使用せざるを得なくなります。また、ハードウェアサポートがある場合でも、実行時のフラグ切り替えによって非正規化数をゼロにフラッシュするように設定する事が可能であり、std::numeric_limits::has_denorm
は必ずしもコンパイル時に確定するプロパティでは無い面があります。
std::numeric_limits::has_denorm_loss
は非正規化数が作成され(計算に使用され)る場合に起こる精度の低下をどのように検出できるかを取得するものです。非正規化数が使用されたことによって精度が低下した時にそれを検出する次の2つの方法がIEEE754標準で指定されていました
- 非正規化損失(Denormalization loss)
- 不正確な結果(Inexact result)
実際には1つ目の実装は存在しなかったため、現在のIEE754からは削除されており、2つ目の実装だけが存在しています。std::numeric_limits::has_denorm_loss
はこの2つのどちらがその環境の浮動小数点数型で実装されているかを示すものでしたが、このような理由によりもはや意味がありません。また、この値は実装によってなぜか異なっています(MSVCだけが浮動小数点数型に対してtrue
を返す)。
これらの理由から、std::numeric_limits::has_denorm
とstd::numeric_limits::has_denorm_loss
は有用なものではなく、最悪勘違いして使用される危険性があるため、非推奨化しようとする提案です。ただし、削除してしまうと互換性の問題を引き起こすため、非推奨に止めようとしています。
P2615R0 Meaningful exports
無意味なexport
を行えないようにする提案。
現在のexport
宣言にまつわる規定の解釈の一つとして、次のような宣言が許可されているように見えます。
// これは何? template export void f(); export template void f(); // 本体の関数テンプレートがexportされているならこちらには不要 export template<> void g(int); template<> export void g(int); // プライマリテンプレートがexportされていれば不要 export template<class T> struct trait<T*>;
この問題はコア言語のissueとして提起され、この提案はその解決のための文言変更を含んだものです。
ただし、この後でもexport {...}
の中でこれらの宣言が現れたとしてもエラーにならないようにされています。export
ブロック内では利便性向上のために、本来export
できない宣言が含まれていても単に無視されるようにされる(ようにする傾向にある)ためです。
P2616R0 Making std::atomic
notification/wait operations usable in more situations
std::atomic
のnotify_one()
とwait()
操作を使いづらくしている問題を解消する提案。
std::atomic
のnotify_one()/wait()
操作はC++23で追加され、std::atomic
オブジェクトを介したスレッド間の同期プリミティブとして利用できます。
ただ、待機しているスレッドを起床させるnotify_one()
操作とstd::atomic
オブジェクトの出力(.store()
)が分かれていることによって、 これを利用した同期プリミティブの移植可能な実装を妨げています。
例えば、std::atomic
のnotify_one()/wait()
のよくある使用法では、std::atomic
オブジェクトへ値を出力してからnotify_one()
を呼ぶという手順がとられます。この場合に、待機するスレッドがwait()
からの復帰時に同期に使用していたstd::atomic
オブジェクトをすぐに破棄する場合に問題が起こります。
待機するスレッドでwait()
が呼ばれる前に(正確には、その呼び出しで値のチェックが行われる前に)、通知スレッド(notify_one()
を呼ぶスレッド)でstd::atomic
オブジェクトへの値の出力が行われていた場合、待機スレッドのwait()
はすぐにリターンし使用していたstd::atomic
オブジェクトの破棄が行われます。すると、通知スレッドではそのように破棄されてしまったstd::atomic
オブジェクトに対してnotify_one()
を呼ぶ可能性があり、これはいうまでもなく未定義動作です。
#include <atomic> #include <thread> int main() { { // 同期用アトミックオブジェクト std::atomic<bool> sync = false; std::thread{[&sync]{ // 値をtrueに更新してから sync.store(true); // #1 // 待機スレッドを起床させる sync.notify_one(); // #2 }}.detach(); // 値が更新(trueになる)されるまで待機 sync.wait(false); // #3 // 終わったら即リターン、syncは破棄される } // #4 }
.wait()
では引数に渡された値と現在の値を比較して、等しい場合にブロッキングし、等しく無い場合はすぐリターンします。この例では処理が#1 -> #3 -> #4 -> #2
の順番で起こる可能性があり、起こった場合に未定義動作となります。
この例は恣意的に見えますが、例えばstd::atomic
を用いてstd::binary_semaphore
が実装されていた場合、このことは表面化しませんが同様の問題を潜在的に引き起こします。
#include <semaphore> #include <thread> int main() { { // binary_semaphoreがstd::atomicを用いて実装されていたとすると・・・ std::binary_semaphore sync; std::thread{[&sync]{ sync.release(); }}.detach(); sync.acquire(); } }
このコードだと先ほどよりも問題が見えにくくなっています。std::atomic
とnotify_one()/wait()
を用いて他の同期プリミティブを実装する場合はこの問題を避けるための工夫が必要になり、それらのワークアラウンドはパフォーマンスを損ねたり移植性が無かったりと問題があります。
実は標準ライブラリの主要3実装(GCC/clang/MSVC)におけるstd::binary_semaphore
(std::counting_semaphore
)はまさにstd::atomic
を利用して実装されています。ただし、そこではstd::atomic
オブジェクトのアドレスのみを使用して値にアクセスしないため、上記のようなライフタイムにまつわる問題は起こりません。ただしこれは、この3つの実装がプラットフォームの対応する操作に関する追加の知識を仮定できるために可能になっているだけで、その他の標準ライブラリ実装がこの方法を取るかどうかはわからず、ユーザーは同様の仮定のもとでstd::atomic
を使用してstd::binary_semaphore
のようなものを安全かつ移植可能に実装することはできません。
この提案はこの問題の解決を図るもので、次の2つの解決策を提示しています。
- 名前空間スコープの
std::atomic_notify_one()
/std::atomic_notify_all()
の規定を変更して、生存期間が終了しているstd::atomic
オブジェクトへのポインタを渡されるようにする。- 渡されたポインタにはアクセスしないことを保証する
std::atomic
オブジェクトの.store()
を呼び出す可能性がある関数ごとに、通知操作を融合したオーバーロードを追加する。std::memory_notification
列挙体を追加して、それを引数に取るようにする
1つ目の方法では、最初のサンプルコードは次のように書き換えられます
#include <atomic> #include <thread> int main() { { // 同期用アトミックオブジェクト std::atomic<bool> sync = false; std::thread{[&sync]{ // 破棄される前にアドレスを取得 auto* pa = &sync; // 値をtrueに更新 sync.store(true); // 通知 std::atomic_notify_one(pa); }}.detach(); // 値が更新(trueになる)されるまで待機 sync.wait(false); }
この時、ポインタpa
をpa
の参照先オブジェクトが破棄された後で使用することが有効であるかには議論があり、Pointer lifetime-end zapという問題として知られています(詳細は以前の記事参照)
- P1726R4 : Pointer lifetime-end zap - WG21月次提案文書を眺める(2020年07月)
- P2414R0 Pointer lifetime-end zap proposed solutions - WG21月次提案文書を眺める(2021年07月)
したがって、この解決策を適用するためにはコア言語にこれらの提案による解決が導入される必要があります。
2つ目の方法では、次のような列挙体とその定数を標準ライブラリに追加し、.store()
などの値を変更する関数にこれを受け取るオーバーロードを追加します。
namespace std { enum class memory_notification : unspecified { notify_none = unspecified, notify_one = unspecified, notify_all = unspecified }; inline constexpr auto memory_notify_none = memory_notification::notify_none; inline constexpr auto memory_notify_one = memory_notification::notify_one; inline constexpr auto memory_notify_all = memory_notification::notify_all; }
この方法では、最初のコードは次のようになります
#include <atomic> #include <thread> int main() { { // 同期用アトミックオブジェクト std::atomic<bool> sync = false; std::thread{[&sync]{ // 値をtrueに更新して通知 sync.store(true, std::memory_notify_one); }}.detach(); // 値が更新(trueになる)されるまで待機 sync.wait(false); } }
実際の実装ではstd::atomic
オブジェクトのアドレスを取ってからストア操作と通知操作を行う(1のような方法)が取られる可能性がありますが、それは実装定義の振る舞いとして(現在のstd::counting_semaphore
の実装のように)動作が保証されるため、ユーザーコードで同じことをした場合の未定義動作を回避することができます。
P2617R0 Responses to NB comments on DTS 12907 "Extensions to C++ for Transactional Memory Version 2"
Transactional Memory TS2に寄せられたNBコメントを受けての修正を反映する提案。
6つのNB(national body)コメント(WG21の各国毎のサブグループからのレビュー結果みたいなもの)が寄せられ、その指摘に対処するための文言変更が含まれています。どうやら全てカナダの委員会メンバからのものです。
この提案は既に2022年7月の全体会議で承認されたようです。
P2618R0 C++ Standard Library Issues to be moved in Virtual Plenary, Jul. 2022
今回(2022/07)の会議で採択された標準ライブラリについてのIssue報告とその解決。
- 3564.
transform_view::iterator<true>::value_type
anditerator_category
should useconst F&
- 3617.
function/packaged_task
deduction guides and deducing this - 3656. Inconsistent bit operations returning a count
- 3659. Consider ATOMIC_FLAG_INIT undeprecation
- 3670. Cpp17InputIterators don't have integer-class difference types
- 3671.
atomic_fetch_xor
missing from stdatomic.h - 3672.
common_iterator::operator->()
should return by value - 3683.
operator==
forpolymorphic_allocator
cannot deduce template argument in common cases - 3687.
expected<cv void, E>
move constructor should move - 3692.
zip_view::iterator
'soperator<=>
is overconstrained - 3701. Make
formatter<remove_cvref_t<const charT[N]>, charT>
requirement explicit - 3702. Should
zip_transform_view::iterator
removeoperator<
? - 3703. Missing requirements for
expected<T, E>
requiresis_void<T>
- 3704. LWG 2059 added overloads that might be ill-formed for sets
- 3705. Hashability shouldn't depend on
basic_string
's allocator - 3707.
chunk_view::outer-iterator::value_type::size
should return unsigned type - 3708.
take_while_view::sentinel
's conversion constructor should move - 3709. LWG-3703 was underly ambitious
- 3710. The end of
chunk_view
for input ranges can beconst
- 3711. Missing preconditions for
slide_view
constructor - 3712.
chunk_view
andslide_view
should not bedefault_initializable
- 3713. Sorted with respect to comparator (only)
- 3715.
view_interface::empty
is overconstrained - 3719. Directory iterators should be usable with default sentinel
- 3721. Allow an arg-id with a value of zero for width in std-format-spec
- 3724. decay-copy should be constrained
P2620R0 Lifting artificial restriction on universal character names
ユニコード文字名によって指定するユニバーサルキャラクタ名(名前付文字エスケープ)を識別子に使用した時の制限を解除する提案。
名前付文字エスケープ(Named character escape)はC++23で導入されたもので、U'\N{LATIN CAPITAL LETTER A WITH MACRON}'
のようにユニバーサルキャラクタ名を指定するものです。詳細は以前の記事を参照
この提案の指摘している問題とは次のようなものです
int main() { auto \N{LATIN CAPITAL LETTER I} = 42; // ng、Iはユニバーサルキャラクタ名で指定できない auto \N{LATIN CAPITAL LETTER I WITH DOT ABOVE} = 42 ; // ok }
LATIN CAPITAL LETTER I
とはI
(U+0049
)の文字(アルファベットのI)であり、これは基本文字集合に含まれる文字であるためユニバーサルキャラクタ名によって指定できません。LATIN CAPITAL LETTER I WITH DOT ABOVE
はIの上にドットがついている文字İ
(U+0130
)で、これは基本文字集合に含まれ無いためユニバーサルキャラクタ名によって指定することができます。
これらのことは、文字/文字列リテラル内では区別されないため問題になりませんが、識別子で使用された時だけこのような違いが生じます。この提案は、この制限を取り払おうとするものです。
P2621R0 UB? In my Lexer?
字句解析するだけで未定義動作を引き起こすものについて、未定義ではなくする提案。
この提案によれば、次のようなコードは規格的には未定義動作となるようです
int \\ // UB : 複数行にわたるユニバーサル文字名 u\ 0\ 3\ 9\ 1 = 0; #define CONCAT(x, y) x ## y int CONCAT(\, u0393) = 0; // UB: マクロ展開によって形成されるユニバーサル文字名 // UB: 閉じていない文字列リテラル const char * foo = "
この提案は、これらの未定義動作を実際の実装に合わせる形で振る舞いを定義しようとするものです。
UB | GCC | clang | EDG | MSVC |
---|---|---|---|---|
複数行UCN | Supported | Supported | Error | Supported |
## によるUCNの形成 |
Supported | Supported | Supported | Supported |
閉じていない文字(列)リテラル | ill-formed | ill-formed | ill-formed | ill-formed |
これらのことを踏まえて、この提案は3つのUBを次のようにしようとしています
UB | 提案 |
---|---|
複数行UCN | Well-formed |
## によるUCNの形成 |
Well-formed |
閉じていない文字(列)リテラル | ill-formed |
従って、MSVCの複数行UCN実装だけがこの提案の影響を受けます。しかし、現在はエラーになっているのでその影響は破壊的なものではありません。
P2622R0 Core Language Working Group "ready" Issues for the July, 2022 meeting
今回(2022/07)の会議で採択されたコア言語についてのIssue報告とその解決。
- 2355. Deducing noexcept-specifiers
- 2405. Additional type-dependent expressions
- 2507. Default arguments for operator[]
- 2534. Value category of pseudo-destructor expression
- 2535. Type punning in class member access
- 2540. Unspecified interpretation of numeric-escape-sequence
- 2571. Evaluation order for subscripting
- 2582. Differing member lookup from nested classes
- 2585. Name lookup for coroutine allocation
- 2586. Explicit object parameter for assignment and comparison
- 2594. Disallowing a global function template main
- 2597. Replaceable allocation and deallocation functions in the global module
- 2606. static_cast from "pointer to void" does not handle similar types
- 2608. Omitting an empty template argument list
P2623R0 implicit constant initialization
一時オブジェクトへの参照によるダングリング発生を削減する提案。
一時オブジェクトへの参照によってダングリング参照が発生するのは主に次の2つの場合です
- 関数から返された参照
- 関数から返された、値のセマンティクスを持たないオブジェクト(
std::string_view
など)
例えば2つ目の場合だと、次のようなコードで簡単にダングリング参照を生成できます
using namespace std::string_literals; int main () { std::string_view sv = "hello world"s; // この行以降svはダングリング参照となる、その使用はUB }
"hello world"s
はユーザー定義リテラルs
によってstd::string
の一時オブジェクトを生成します。それをstd::string_view
でバインドすると、すぐにその一時オブジェクトの寿命が尽きてダングリングとなります。
この提案の目的は、このコードがUB(ダングリング参照)にならないようにすることです。この例では、定数式"hello world"s
が通常の文字列リテラルと同様に静的記憶域期間(static storage duration)を持つようにする(暗黙的な定数初期化を行う)ことで、ダングリング参照の生成を防止しようとしています。
また、std::string_view
によるダングリングは次のように間接的に発生する場合もあります
std::string operator+(std::string_view s1, std::string_view s2) { return std::string{s1} + std::string{s2}; } auto f() { std::string_view sv = "hi"; sv = sv + sv; // svはダングリング ... }
sv + sv
の結果はstd::string
の一時オブジェクトであり、その寿命はその式の終わり(;
)までです。
この提案では、この場合にこの式の結果生成される一時オブジェクトの生存期間をその式ではなく囲むブロックのスコープとバインドさせることで、このようなダングリングを防止しようとしています。
もちろん、この場合でもこの参照(sv
)をこのブロックの外に持ち出してしまえばダングリング参照となりますがそれは現在でも同じことで、この提案の目的はダングリング参照の発生を抑制することにあり、完全に失くすことを目指してはいません。
1つ目の場合(関数の参照戻り値)では、次のような場合にダングリング参照を生成できます
struct X { int a, b; }; const int& f(const X& x) { return x.a; } // 引数のメンバへの参照を返す int main() { const int& a = f({4, 2}); // 一時オブジェクトを引数で与える // aはダングリング参照、UB }
この時でも、{4, 2}
の一時オブジェクトの生存期間が囲むブロックのスコープに紐づいていれば、ダングリング参照は生成されません。
std::string
では、この例とよく似たことを簡単に起こすことができます。
char& c = std::string{"hello my pretty long string"}[0]; c = 'x'; // cはダングリング参照、UB std::cout << "c: " << c << '\n'; // cはダングリング参照、UB
この時でも一時オブジェクトの生存期間がその式ではなく囲むブロックに紐づいていればダングリングを回避できます。ここで、このような一時オブジェクトの寿命にまつわる問題を回避するために一時オブジェクトを変数に受けてみると現在の一時オブジェクトの生存期間のルールがプログラマの期待と一致していないことが垣間見えます。
auto anonymous = std::string{"hello my pretty long string"}; char& c = anonymous[0]; c = 'x'; // ok、cはダングリングではない std::cout << "c: " << c << '\n'; // ok、cはダングリングではない
これはプログラマから見ればほぼ同じコードですが、このように一時オブジェクトに名前づけをするだけでダングリング参照の生成を回避できます。しかしこれは、一時オブジェクトの生存期間を囲むブロックに紐づけるという操作を手動でやっているだけです。
このようなことが意図せず発生しうるものとして範囲for
がよく知られています。
for (auto x : reversed(make_vector())) { ... }
make_vector()
がstd::vector
の右辺値を返し、reversed()
がstd::ranges::owning_view
のような一時オブジェクトの生存期間延長のためのケアをしない場合、この範囲for
全体はダングリングした範囲をイテレートします。
例えば、範囲for
は次のように展開されています
{// containing block auto&& rg = reversed(make_vector()); // この行でmake_vector()の戻り値の寿命が尽きる auto pos = rg.begin(); auto end = rg.end(); for ( ; pos != end; ++pos ) { auto x = *pos; ... } }
この時でも、一時オブジェクトの生存期間が囲むブロックに紐づいていれば、このようなUBを回避できます。そのことは、一時オブジェクトに明示的に名前を与えてみるとわかります
{// containing block auto anonymous1 = make_vector(); auto anonymous2 = reversed(anonymous1); auto pos = anonymous2.begin(); auto end = anonymous2.end(); for ( ; pos != end; ++pos ) { auto x = *pos; ... } }
この提案の主張することは、プログラマから見れば一時オブジェクトとは名前のない変数であるということです。その観点から、一時オブジェクトの寿命を通常の変数のようにすればダングリング参照の発生を減らすだけでなく、ダングリングする可能性のある関数戻り値を受けるための余計な変数の名前づけを削減することもできます。これによって、一時オブジェクトに注意して関数戻り値を命名するのではなく、一時オブジェクトのまま使用することを奨励することすらできるようになります。
より詳細には、この提案ではこれらの一時オブジェクトのうち、暗黙的な定数初期化が可能な場合(constexpr
コンストラクタを持つ型のconst
参照)にはコンパイル時に定数初期化して静的記憶域期間を与えることで一時オブジェクトではなくし、そのような定数初期化ができない一時オブジェクトについてはその寿命を囲むブロックスコープにまで延長(通常の名前付き変数と同様に)することで、ダングリング参照の発生を防止しようとしています。
// std::mapからキーに対応する値を取得する、なければ指定したデフォルトを返す const V& findOrDefault(const std::map<K,V>& m, const K& key, const V& defvalue); void f() { std::map<std::string, std::string> myMap; const std::string& s = findOrDefault(myMap, key, "none"); // "none"はstd::stringの一時オブジェクト // 現在はsはダングリング参照 // この提案後は定数初期化されたグローバルな"none"(std::stringオブジェクト)を指す } std::string make_str(); // 非constexpr関数 void g() { std::map<std::string, std::string> myMap; const std::string& s = findOrDefault(myMap, key, make_str()); // 実行時文字列で使用した場合 // 現在はsはダングリング参照 // この提案後はmake_str()の戻り値の一時オブジェクトの寿命は囲むスコープに拡張されるため、ダングリングではなくなる }
一時オブジェクトの生存期間を囲むブロックに拡張するのは、現在のC++でも制限的ながら起こっており、このことは全く新しいことではありません。
template<typename T> using id = T; int i = 1; int&& a = id<int[3]>{1, 2, 3}[i]; // 配列の一時オブジェクトの寿命はaの寿命と同期する const int& b = static_cast<const int&>(0); // intの一時オブジェクトの寿命はbの寿命と同期する int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0); // 条件演算子の両方のオペランドの一時オブジェクトの寿命はcの寿命と同期する
この提案の内容は以前のP0936R0を引き継ぐものです。そちらでは追加の注釈によって引数や戻り値の生存期間の延長(この提案の一時オブジェクトの自動変数化)を行なっていましたが、この提案ではそれと同じことを暗黙的に行います。この提案はそこに一時オブジェクトの暗黙的定数初期化を追加することでP0936を補強するとともに、P0936の内容だけでは適切な対策とならないものについてより安全にしようとするものです。
P2624R0 Make operations on bools more portable
bool
型に関する標準の矛盾を正す提案。
標準では、bool
型について次のように指定しています
- 実装定義の符号なし整数型と同じオブジェクト表現、値表現、アライメントを持つ
bool
型の値はtrue
とfalse
型T
のオブジェクト表現とはT
のオブジェクトをunsigned char[N]
で参照した時のバイト列のことで、T
の値表現とはT
の値を保持するビット列のことです。
bool
の値は2つしか取れないとする場合、少なくとも256の異なる値を取れる符号なし整数型と同じ値表現として実装することはできません。この矛盾によって、bool
型のどのような実装も標準に適合することはできておらず、さまざまな方法で標準の規定を近似しています。
- clang : 1バイトオブジェクト中の1ビットのビットフィールドであるかのように実装
- 下位1ビットのみが値に関与し、残りはパディングビット
- 2つの値のみを取れるという規定を満たすものの、ベースとなる符号なし整数型と同じ値表現を持たない
- GCC/MSVC :
enum bool : unsigned char { false, true };
のような型として実装- 符号なし整数型と同じ値表現を持つものの、異なる256の値を持つことができる
この違いによって、微妙な振る舞いの違いを観測することができます。
// clangは常に 0 or 1のどちらかを返す // msvc & gccは 0, 1, -1のどれかを返す int test1(bool b) { switch(b) { case false: return 0; case true: return 1; default: return -1; } } // clang & msvcは常に 1を返す // gccは 1か2のどちらかを返す int test2(bool b) { int n = 0; if (b) n++; if (!b) n++; return n; } // clangは常に 0 or 2のどちらかを返す // msvc & gccは 0~510の間の任意の値を返す int test3(bool b) { return b + b; } // clang & msvcは常に 0 or 1のどちらかを返す // gccは 0~255の間の任意の値を返す int test4(bool b) { return b || b; }
この提案の目的は、これらのことを正すことでbool
を含む式が数学的論理と一致した予測可能で意外性のない結果を返すようにし、bool
型の変数を安全かつポータブルに使用可能とすることです。
この提案の内容は、clangは既に準拠していますがGCC/MSVCはそうではなく、GCC/MSVCの現在の挙動に依存しているコードは動作が変更されることになります。