P0780R0: Pack expansion in lambda init-capture
lambda式のinit-captureにpack expansionを認める提案。ムーブができるようになる。
template < typename ... Types >
void f( Types ... args )
{
auto lambda = [ args = std::move(args)... ] { return g( args...) ; }
}
P0781R0: A Modern C++ Signature for Main
mainのシグネチャーを近代化する提案。
int main( const 何らかのコンテナー型<何らかの文字列型> args ) ;
にしたい。この提案では、std::initializer_list<std::string_view>を推奨している。
たしかに、そろそろmainも近代化されるべきだ。
P0782R0: A Case for Simplifying/Improving Natural Syntax Concepts
コンセプトを引数に取る関数テンプレートの文法を使った関数の依存名の解決は、コンセプトで示されているものしかできないようにしようという提案。
現在のコンセプトは、制約テンプレートの使用者に対するチェックだけで、制約テンプレート内のチェックはない。
例えば、現状のコンセプトは以下のコードが通る。
template < typename T >
concept has_f = requires( T x ) { x.f() ; }
void call_f( has_f & x )
{
// コンセプトに書いてある
x.f() ;
// OK、ただしコンセプトに書いてない
x.g() ;
}
struct X
{
void f() ;
void g() ;
}
int main()
{
X x ;
call_f( x ) ;
}
コンセプトによるチェックがないと、以下のようなコードが通ってしまう。
template < typename T >
concept has_const_f = requires( T x ) { const_cast<const T &>(x).f() ; }
void call_f( has_const_f & x )
{
x.f() ;
}
struct X
{
void f() ;
void f() const ;
} ;
int main()
{
X x ;
// 非const版のvoid X::f()が呼ばれる
f( x ) ;
}
コンセプトは制約テンプレートを制限しないので、このような挙動になってしまう。
文書では、初心者にコンセプトを使いやすいようにし混乱を防ぐために、ドラフト入りするときに削られたtarse notationによる関数制約テンプレートを復活させ、その依存名の解決はコンセプトに合致するものしか行わないようにしようという提案をしている。
tarse notationを使わないものと使うもので挙動が違うのはますます混乱の元ではないか。
P0783R0: P0783: Continuations without overcomplicating the future
現在、futureに継続を追加するための議論が行われているが、結局executorなしでは継続が実行される媒体が決定的ではなく意味がない。Facebookはfollyライブラリで継続とexecutorを持つfutureを実装して、社内で使った経験から、この件について意見している。
P0784R0: Standard containers and constexpr
vectorやunordered_mapのような可変サイズのコンテナーは実行時プログラミングに便利だ。ということは、コンパイル時計算にも便利なはずだ。
constexpr版のvectorやstringが提案されているが、vectorをそのままconstexr化することはできないのか。
vectorのconstexpr化を妨げる処理は3つ。
- デストラクター
- メモリ確保
- in-place new
デストラクターはconstexpr化できないが、この制約は取っ払うことができる。EDG, MSVC, GCC, Clangの開発者は皆同意している。
メモリ確保はコンパイラーがコンパイル時のメモリ確保を特別に処理することでconstexpr化できる。しかし、コンパイル時メモリ確保は、未定義の挙動を完全に検出できなければならないので、アドレスには追加のメタデータを付与しなければならない。これによりポインターのvoid *への変換はできなくなる。
問題は、いまのoperator newの宣言が以下のようになっていることだ。
void * operator new( std::size_t ) ;
ただし、new, deleteを使っているのならばコンパイラーが特別に対応することもできる。あるいは標準アロケーターで対処する。
in-place newをすべてconstexpr化することは無理だが、vectorで使う一部の用例はconstexpr化できる。
さて、コンパイル時計算で確保したメモリを開放しない場合どうなるのか。一番簡単な方法は未開放を禁止するということだ。しかし、コンパイル時処理の結果を実行時に渡したいときもあるだろう。すると開放されなかったコンパイル時メモリ確保はstaticストレージとして残すという手もある。
夢が広がる話だ。あらゆる計算はコンパイル時計算になりうる。
P0785R0: P0785R0: Runtime-sized arrays and a C++ wrapper
実行時サイズ配列復活論。CのVLAのサブセットとC++のラッパークラスの提案。
ようするにスタックから実行時に指定したサイズのメモリを確保したいということだ。
かつてC++14に入りそうだったdynarryは、スタックから確保できない場合、動的確保にフォールバックするという設計で、確実にスタックから確保されてほしい利用者にとって当てにできない設計であった。その後、厳格にスタックからしか確保しないフォールバックのないbs_arrayなども提案されたりしていた。
C++規格では、スタックとヒープという存在を避けてきた。代わりに、オブジェクトは寿命期間を持つストレージで区別されてきた。殆どのコンパイラーはスタックとヒープを使っていて、その使い分けは自然なものだ。しかし、世の中には組み込みとかカーネルなどの極端な環境があり、スタックメモリーのサイズが極端に小さい環境もあるかもしれない。そういう環境ではメモリはヒープに確保し、スタックではヒープメモリへのアドレスを格納するだけという実装もあり得る。結局、C++規格としてはスタックの利用を強制することはできない。
[PDF] P0786R0: ValuedOrError and ValueOrNone types
エラー処理を暗号的ではなくなるが冗長な記述になるライブラリの提案。
提案されているライブラリの一部を使うと、以下のように書ける。
// 同じ
x.has_value() ;
value_or_error::has_value(x) ;
// 同じ
(o) ? f(*o) : nullopt ;
value_or_error::transform(o, f) ;
// 同じ
(!e) ? e.error() : err
value_or_error::error_or(e, err)
// 同じ
(e) ? false : e.error() == err ;
value_or_error::check_error(e, err) ;
暗号的なコードではなくなるが、冗長な記述になるし、意味がわかりやすいかというとそれも疑問だ。
[PDF] P0787R0: Proclaimed Ownership Declarations
モジュールからproclaimed-ownership-declarationを削除する提案。
[PDF] P0788R0: Standard Library Specification in a Concepts and Contracts World
「プログラムの挙動に合わせて仕様を変えるのは、その逆より簡単だ」 Alan Perlis
「8.5×11インチの紙1枚に収まらない仕様は理解不可能だ」 Mark Ardis
「specというのは仕様(specification)の略か? 憶測(speculation)の略なんじゃないか?」 詠み人知らず
標準ライブラリはどのようにconceptに対応すべきかという方針の提案。
Requires: は現状維持。代わりに新しい要素を追加する。
Requires: を段階的に廃止し、新しい要素で置き換える。
新しい要素として、Constraitns:, Diagnostics:, Expects:, Ensures:を追加する。
[PDF] P0789R0: Range Adaptors and Utilities
Rangeアダプターの提案。
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0