Skip to content

Commit

Permalink
fix: fix code to apply sample tag
Browse files Browse the repository at this point in the history
  • Loading branch information
yumetodo committed Dec 5, 2017
1 parent b91ecfa commit fcf8708
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 15 deletions.
14 changes: 12 additions & 2 deletions lang/cpp17/if_constexpr.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class C {

`if constexpr`文で、実行されない方の`statement``discarded statement`(破棄文)となり、文の実体化を防ぐ。言い換えると、Two Phase Name Look-upにおける`dependent name`(依存名)は、`discarded statement`の場合検証されない。また文が実体化されないのだから通常のif文と同じくもちろん実行時に実行もされない。つまり次の例は意図と異なる挙動を示す。

```cpp
```cpp example
#include <type_traits>

template<typename T>
Expand All @@ -81,11 +81,16 @@ void f()
static_assert(false) ;
}
}
int main()
{
f(2.4);
f(3);
}
```

なぜならば`discarded statement`はテンプレートの実体化を防ぐ(依存名の検証をしない)だけで、被依存名は検証されるからである。この例の[`static_assert`](https://cpprefjp.github.io/lang/cpp11/static_assert.html)に渡す条件式はテンプレートパラメータに依存していないので、テンプレートの宣言時に検証され、エラーとなる。言い換えれば`static_assert`に渡す条件式が依存名ならばテンプレートの宣言時に検証されず、テンプレート実体化まで評価を遅らせる事ができる。

```cpp
```cpp example
#include <type_traits>

template <typename T>
Expand All @@ -99,6 +104,11 @@ void f()
static_assert(false_v<T>);
}
}
int main()
{
f(2.4);
f(3);
}
```

`if constexpr`文の条件式内は実体化が起きる。したがって実体化するとコンパイルエラーになるものは書いてはいけない。
Expand Down
36 changes: 23 additions & 13 deletions lang/cpp17/remove_deprecated_increment_of_bool.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,29 @@ C++17では`bool`型に対する前置および後置の`operator ++`を削除

具体的にどのような働きをするのかというと、以下のように値を`true`に書き換える機能をもつ。

```cpp
```cpp example
#include <iostream>
int main()
{
bool b = false;
++b; // => true
++b; // => true
const b1 = ++b;
std::cout << std::boolalpha << b1 << std::endl; // => true
const b2 = ++b;
std::cout << std::boolalpha << b1 << std::endl; // => true
}
```

ここで、前置の`operator ++`は、以下のように置き換えられる:

```cpp
```cpp example
#include <iostream>
int main()
{
bool b = false;
b = true; // => true
b = true; // => true
b = true;
std::cout << std::boolalpha << b << std::endl; // => true
b = true;
std::cout << std::boolalpha << b << std::endl; // => true
}
```

Expand Down Expand Up @@ -79,9 +85,10 @@ C++17ではこれらが削除され、`opeartor ++`の定義(§ 8.2.6 expr.post.

この項は**十分な出典が存在せず推測でしかない**ことに注意して読み進めてほしい。

もともとC++の前身であるC言語(ANSI C89)には`bool`型は存在しなかった。そのために、真理値を`int`型で代用する例が見られた。
もともとC++の前身であるC言語(ANSI C89)には`bool`型は存在しなかった。
そのために、真理値を表すために`bool`型の代わりとして`int`型や`char`型、`unsigned char`型で代用する例が見られた。

```c
```c example
int main(void)
{
int flag = 0;
Expand All @@ -95,14 +102,17 @@ int main(void)
つまり、非0を`true`、0を`false`として扱う。ここで次のようなコードを見てみよう。
```cpp
```cpp example
#include <iostream>
#include <vector>
#include <limits>
//説明の都合上using aliasではなくあえてtypedefを使用します
typedef unsigned char my_bool;
int main()
{
std::vector<int> v{};
//append elements to v
int flag = 0;
std::vector<short> v(static_cast<std::size_t>(std::numeric_limits<my_bool>::max()) + 3);
my_bool flag = 0;
for(auto&& i : v){
if(flag++) std::cout << ',';
std::cout << i << std::endl;
Expand All @@ -111,7 +121,7 @@ int main()
```

これは、最初の要素以外のときは`,`という文字を要素の出力の前に行うことを期待している。しかし期待通りには動かない。
`flag``int`型の最大値になったときif文の条件評価が行われることを考えよう。`flag`のインクリメントはオーバーフローするので未定義動作になるが、殆どの環境で2の補数表現を使っているため、`int`型の最小値になる。すると、`flag`の値が再び0まで加算された際にカンマが出力されない。
`flag``unsigned char`型の最大値になったときif文の条件評価が行われることを考えよう。`flag`のインクリメントはオーバーフローするので未定義動作になるが、殆どの環境で2の補数表現を使っているため、`unsigned char`型の最小値になる。すると、`flag`の値が再び0まで加算された際にカンマが出力されない。

This comment has been minimized.

Copy link
@k-satoda

k-satoda Dec 5, 2017

Contributor

@yumetodo flag の型を int から unsigned char に変更したことで、
インクリメントによるオーバーフロー(およびその結果としての未定義動作)は
起こらなくなっているほか、2の補数表現も関係なくなっていて、
規格で保証される範囲の結果として最大値から 0 にラップするようになって
います。

ラップした時点で 0 なので、つづく「flagの値が再び0まで加算された際」
という記述もちょっと変になっちゃってます。

・・・現状のコードに即した文面に直していくことも考えられますが、
サンプルコードの実行はあんまり重要なものではないと思うので、めんどうなら
ここは元の状態に戻しちゃえばいいんじゃないかと思ってます。

This comment has been minimized.

Copy link
@yumetodo

yumetodo Dec 6, 2017

Author Member

あやや・・・。ちょっと考えます・・・

インクリメントによるオーバーフロー(およびその結果としての未定義動作)は
起こらなくなっているほか

unsignedのときって定義されているんですか?

This comment has been minimized.

Copy link
@faithandbrave

faithandbrave Dec 6, 2017

Member

unsignedな整数 (char含む) のオーバーフロー動作は定義されます。signedは、いまは処理系定義ですね。
https://cpprefjp.github.io/reference/limits/numeric_limits/is_modulo.html

This comment has been minimized.

Copy link
@yumetodo

yumetodo Dec 6, 2017

Author Member

@faithandbrave 情報ありがとうございます。
書き換えます

This comment has been minimized.

Copy link
@k-satoda

k-satoda Dec 6, 2017

Contributor

@yumetodo ・・・書き換えるほうでいきますか。
後出しになっちゃってすいませんが、僕が「めんどうなら」と書いたときに
考えていためんどうな話をひととおりお知らせしておきます。

unsigned char の値の範囲が int の値の範囲に含まれる場合、整数拡張により
インクリメント自体は int で行われるのでオーバーフローは起こらなく
なります。その結果を書き戻す際に unsigned char に変換されるんですが、
これは unsigned char 含む符号無し整数への型変換の動作として is_modulo で
示される結果になります。
https://timsong-cpp.github.io/cppwp/n4659/conv.integral#2

unsigned char の値の範囲が int の値の範囲を超える場合、整数拡張や
型変換は起こりませんが、符号無し整数の特性としてインクリメントの結果が
is_modulo で示される結果になります。

一般的にはどちらも「オーバーフロー」と呼ばれているのを見ますが、規格では
これらを「オーバーフロー」とは呼んでいません。
https://timsong-cpp.github.io/cppwp/n4659/basic.fundamental#footnote-49

規格の用語と不整合を起こさない範囲で説明するとすれば、
「 unsigned char で表現できる範囲を超えた結果、ラップして 0 になる」
とかそんな表現がいいのかなぁ、というところ。

This comment has been minimized.

Copy link
@saki7

saki7 Dec 6, 2017

Contributor

ここはオーバーフローでUBになることが示せれば良いので、元の int に戻す方が適切だと思います。

This comment has been minimized.

Copy link
@yumetodo

yumetodo Dec 6, 2017

Author Member

intだと最大値が大きすぎてオーバーフローの例を示せないんですよね・・・

This comment has been minimized.

Copy link
@saki7

saki7 Dec 6, 2017

Contributor

そういうことでしたか。それなら、コンテナが説明に不要だと思うので、コード自体を単純なforループに変更して、インデックス (i) をカンマ区切りで出力する例に変えるのはどうでしょうか。

This comment has been minimized.

Copy link
@yumetodo

yumetodo Dec 6, 2017

Author Member

それでもintだと最大値が大きすぎて標準出力が大変なことになる気がするんですが・・・

これsigned charに変えるだけじゃだめかな・・・

This comment has been minimized.

Copy link
@saki7

saki7 Dec 6, 2017

Contributor

では、I/O自体が不要だと思います。人が死ぬ方が重要なので、以下のような感じでどうですか?

#include <iostream>

void test_firepower() {
  // 最大出力で火力を試す
  std::cout << "最大火力" << std::endl;
}
void fire() {
  // 通常の火力で ”処理” をする
}

int main()
{
  int tested = 0;
  while (true) {
    if (!tested) {
      test_firepower();
    } else {
      fire();
    }
    tested++;
  }
}

This comment has been minimized.

Copy link
@yumetodo

yumetodo Dec 6, 2017

Author Member

This comment has been minimized.

Copy link
@saki7

saki7 Dec 6, 2017

Contributor

お、サンプルコードの修正対応ありがとうございます。

しかし、いざ自分の例が採用されると恥ずかしいですね…………

This comment has been minimized.

Copy link
@yumetodo

yumetodo Dec 6, 2017

Author Member

個人的には「じゃあ出力しなくていいじゃん」という発想に至らなかったので感謝です。


これに起因するバグで少なくとも6つの過度の放射線被曝事故を引き起こし、3人が死亡した例がある。
Therac-25はカナダ原子力公社(AECL)とフランスCGR-MeV社によって開発・製造された放射線療法機器である。
Expand Down

1 comment on commit fcf8708

@saki7
Copy link
Contributor

@saki7 saki7 commented on fcf8708 Dec 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

めちゃくちゃ助かります

Please sign in to comment.