-
Notifications
You must be signed in to change notification settings - Fork 160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: CWG2518適用を前提として記述する #1260
base: master
Are you sure you want to change the base?
Changes from 2 commits
f1090f2
39695e1
828812e
56695bc
49d191b
3da7c5d
ffb8d64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,11 +68,60 @@ void f(Out& o, A1 const& a1, A2 const& a2) | |
それでも記述が実際にしたい処理に比べて不必要に複雑になる。 | ||
constexpr if文の導入によりそのような複雑な手法を用いずに素直に条件付きのコンパイルを実現できるようになった。 | ||
|
||
`constexpr if`文の条件式内は実体化が起きる。したがって実体化するとコンパイルエラーになるものは書いてはいけない。 | ||
|
||
```cpp example | ||
#include <type_traits> | ||
#include <iostream> | ||
|
||
struct Hoge { | ||
using type = int; | ||
}; | ||
|
||
template <typename T> | ||
void f() | ||
{ | ||
if constexpr (std::is_same_v<T::type, int> || std::is_same_v<T::value_type, int>) { | ||
std::cout << "is int" << std::endl; | ||
} | ||
} | ||
|
||
int main() | ||
{ | ||
f<Hoge>(); //error: Hoge::value_typeは存在しないのでif constexpr文の条件式がコンパイルエラーになる | ||
} | ||
``` | ||
|
||
### `static_assert`文に関する例外 | ||
|
||
後に述べる2段階名前探索に関する注意点とは関係なく、C++23以降、もしくはCWG 2518が適用された環境においては、template文(もしくは適切な特殊化や`constexpr if`文の中の文)が実際にインスタンス化されるまで、`static_assert`文の宣言は遅延される。 | ||
|
||
```cpp example | ||
#include <cstdint> | ||
template <class T> | ||
void f(T t) { | ||
if constexpr (sizeof(T) == sizeof(std::int32_t)) { | ||
use(t); | ||
} else { | ||
static_assert(false, "must be 32bit"); | ||
} | ||
} | ||
|
||
void g(std::int8_t c) { | ||
std::int32_t n = 0; | ||
f(n); // OK: nはstd::int32_t型なので`use(t);`のほうがインスタンス化されるために、static_assert文は宣言されない。 | ||
f(c); // error: cはstd::int8_t型なので、static_assert文は宣言され、"must be 32bit"とコンパイラが診断メッセージを出力する | ||
} | ||
``` | ||
|
||
### 2段階名前探索における注意点 | ||
|
||
`constexpr if`文で、実行されない方の`statement`は廃棄文(discarded statement)となり、文の実体化を防ぐ。言い換えると、2段階名前探索における依存名(dependent name)は、廃棄文の場合検証されない。また文が実体化されないのだから通常のif文と同じくもちろん実行時に実行もされない。つまり次の例は意図と異なる挙動を示す。 | ||
|
||
```cpp example | ||
```cpp | ||
template<bool> struct inferior_static_assert_failure; | ||
template<> struct inferior_static_assert_failure<true>{ enum { value = 1 }; }; | ||
#define INFERIOR_STATIC_ASSERT(B) typedef char inferior_static_assert[sizeof(inferior_static_assert_failure<bool(B)>::value)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 後続趣旨「CWG 2518未適用のstatic_assertでも同じ問題がある」との直接対比可能な構造にしたいのだと推察しますが、 (とコメントしつつ、良い案が思い浮かばず申し訳ない) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 弱い提案:「つまり次の例は意図と異なる挙動を示す。」+ (提案適用後の構成)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
提案についてはちょっと週末寝かせて考えてみます。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 除去することにしました。 |
||
#include <type_traits> | ||
|
||
template <typename T> | ||
|
@@ -82,7 +131,7 @@ void f(T) | |
{ | ||
// Tがintのときのみ評価されてほしい | ||
// 実際は常に評価される | ||
static_assert(false); | ||
INFERIOR_STATIC_ASSERT(false); | ||
} | ||
} | ||
|
||
|
@@ -93,21 +142,19 @@ int main() | |
} | ||
``` | ||
|
||
なぜならば廃棄文はテンプレートの実体化を防ぐ (依存名の検証をしない) だけで、非依存名は検証されるからである。この例の[`static_assert`](/lang/cpp11/static_assert.md)に渡す条件式はテンプレートパラメータに依存していないので、テンプレートの宣言時に検証され、エラーとなる。言い換えれば`static_assert`に渡す条件式が依存名ならばテンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせることができる。 | ||
CWG 2518が適用されていない環境においては`static_assert`でも同じ現象が発生する。 | ||
|
||
```cpp example | ||
#include <type_traits> | ||
|
||
template <typename T> | ||
constexpr bool false_v = false; | ||
|
||
template <typename T> | ||
void f(T) | ||
{ | ||
if constexpr (std::is_same_v<T, int>) | ||
{ | ||
// Tがintのときのみ評価される | ||
static_assert(false_v<T>); | ||
// Tがintのときのみ評価されてほしい | ||
// 実際は常に評価される | ||
static_assert(false); | ||
} | ||
} | ||
|
||
|
@@ -118,18 +165,25 @@ int main() | |
} | ||
``` | ||
|
||
上の例では`false_v`を作ったが、ラムダ式でも同様のことができる。ラムダ式はそれが記述された位置から見て最小のスコープ (ブロックスコープ/クラススコープ/名前空間スコープ) で宣言されるクラスとして扱われる。例えば、下の例では`f()`という関数テンプレート内で宣言される。関数テンプレート内のクラスは依存名になるため、テンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせることができる。 | ||
なぜならば廃棄文はテンプレートの実体化を防ぐ (依存名の検証をしない) だけで、非依存名は検証されるからである。この例の[`static_assert`](/lang/cpp11/static_assert.md)に渡す条件式はテンプレートパラメータに依存していないので、テンプレートの宣言時に検証され、エラーとなる。 | ||
|
||
#### CWG 2518が適用されていない環境での回避策 | ||
|
||
言い換えれば`static_assert`に渡す条件式が依存名ならばテンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせることができる。 | ||
|
||
```cpp example | ||
#include <type_traits> | ||
|
||
template <typename T> | ||
constexpr bool false_v = false; | ||
|
||
template <typename T> | ||
void f(T) | ||
{ | ||
if constexpr (std::is_same_v<T, int>) | ||
{ | ||
// Tがintのときのみ評価される | ||
static_assert([]{return false;}()); | ||
static_assert(false_v<T>); | ||
} | ||
} | ||
|
||
|
@@ -140,51 +194,25 @@ int main() | |
} | ||
``` | ||
|
||
`constexpr if`文の条件式内は実体化が起きる。したがって実体化するとコンパイルエラーになるものは書いてはいけない。 | ||
上の例では`false_v`を作ったが、ラムダ式でも同様のことができる。ラムダ式はそれが記述された位置から見て最小のスコープ (ブロックスコープ/クラススコープ/名前空間スコープ) で宣言されるクラスとして扱われる。例えば、下の例では`f()`という関数テンプレート内で宣言される。関数テンプレート内のクラスは依存名になるため、テンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせることができる。 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 弱い意見:古いコンパイラへに対する回避策提示は、P2593R1 で言及されている信頼性の高い方式1つで十分だと思います。
本件に関連する言語仕様解釈は難解なため、条件部にラムダ式を用いる遅延false実装の妥当性について、私個人(yohhoy)は確証をもてません。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. その記述はもともと constexpr if と local struct の評価のタイミングについてでの @yohhoy さんの回答がもとになっていた気がしますね・・・。cppreffjpに書くのを提案したのは私ですが・・・。 回避策として複数あげる必要性が薄らいだのは確かですが、積極的に除去する動機が自分にはないです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 本コメントの趣旨は「C++言語リファレンスという特性を踏まえると、古い処理系向けの回避策として複数選択肢を提示し読者を迷わせる必然性はない」です。 PS. teratailサイトでの回答を引き合いに出されていますが、これは2018年当時の質問に答えただけです。この部分については私としては困惑しかありません。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 編集方針 - 正確なものに不要な情報はない という話も…。(追記: 個人的にはどっちつかずです) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 時間を開けて読み返した感じ、ないほうがスッキリするので思い切って消しました。うまくハマるように復活させられるならばそういう編集を後の人が行うことを止めるものではありません。 |
||
|
||
```cpp example | ||
#include <type_traits> | ||
#include <iostream> | ||
|
||
struct Hoge { | ||
using type = int; | ||
}; | ||
|
||
template <typename T> | ||
void f() | ||
void f(T) | ||
{ | ||
if constexpr (std::is_same_v<T::type, int> || std::is_same_v<T::value_type, int>) { | ||
std::cout << "is int" << std::endl; | ||
if constexpr (std::is_same_v<T, int>) | ||
{ | ||
// Tがintのときのみ評価される | ||
static_assert([]{return false;}()); | ||
} | ||
} | ||
|
||
int main() | ||
{ | ||
f<Hoge>(); //error: Hoge::value_typeは存在しないのでif constexpr文の条件式がコンパイルエラーになる | ||
} | ||
``` | ||
|
||
### (CWG 2518が適用された環境) `static_assert`文に関する例外 | ||
|
||
上に述べたように、`constexpr if`文の中の文は廃棄文においても、非依存名の検証を行う。このため特に`static_assert`文を使う時に直感的ではない挙動を示していた。 | ||
|
||
C++23以降、もしくはCWG 2518が適用された環境においては、template文(もしくは適切な特殊化や`constexpr if`文の中の文)が実際にインスタンス化されるまで、`static_assert`文の宣言は遅延される。 | ||
|
||
```cpp example | ||
#include <cstdint> | ||
template <class T> | ||
void f(T t) { | ||
if constexpr (sizeof(T) == sizeof(std::int32_t)) { | ||
use(t); | ||
} else { | ||
static_assert(false, "must be 32bit"); | ||
} | ||
} | ||
|
||
void g(std::int8_t c) { | ||
std::int32_t n = 0; | ||
f(n); // OK: nはstd::int32_t型なので`use(t);`のほうがインスタンス化されるために、static_assert文は宣言されない。 | ||
f(c); // error: cはstd::int8_t型なので、static_assert文は宣言され、"must be 32bit"とコンパイラが診断メッセージを出力する | ||
f(2.4); | ||
f(3); | ||
} | ||
``` | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
表記揺れ。インスタンス化→実体化
表記揺れ。template → テンプレート? ところで template 文って何のことかよくわからないので具体的にできますか。
CWG 2518 を確認したら、"テンプレート定義時に式が評価された場合は static_assert 宣言は効果がない" と言っていて、別に評価や検証が遅延されるわけではないみたいです。つまり「(2段階名前探索の) 1段階目で false になっても何も起こらない」という形みたいです。
「宣言」って書いている時は何を意味していますか? この文書の他の部分ではテンプレートの定義時点のことを宣言と言っているように見えますが、ここでは
static_assert
の「実体化」もしくは「検証」のことを言っている?あと「
static_assert
文」ではなくて「static_assert
宣言」のような…。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
構文要素としての
static_assert
は、文(statement)ではなく宣言(declaration)の一種ですね。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
で対応してみました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これは対応する予定はありますか。P2593R1 の対応する記述に従えば "template 文" ではなくて "テンプレート” でしょうか (edit:
lang/cpp11/static_assert.md
にも同様のものがありますね)。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あと、これについてもご確認いただけたらと思います。