-
Notifications
You must be signed in to change notification settings - Fork 172
refactor: CWG2518適用を前提として記述する #1260
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
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)] | ||
|
||
#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()`という関数テンプレート内で宣言される。関数テンプレート内のクラスは依存名になるため、テンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせることができる。 | ||
|
||
|
||
```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.
で対応してみました。
Uh oh!
There was an error while loading. Please reload this page.
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.
あと、これについてもご確認いただけたらと思います。