View
19.656
Download
1
Category
Preview:
DESCRIPTION
Boost.勉強会 #8 中3女子が狂える本当に気持ちのいい constexpr
Citation preview
中3女子中3女子が狂えるが狂える
本当に本当に 気持ちのいい気持ちのいい
constexprconstexpr
Boost.勉強会
#8bolero_MURAKAMI2012/2/11
◆⾃⼰紹介•
名前
:
村上
原野
(むらかみ
げんや)
@bolero_MURAKAMI, id:boleros
•
棲息地:
⼤都会岡⼭
•
仕事
:
猪⾵来美術館
陶芸指導員・普段はやきものの修⾏をしたり、
縄⽂⼟器をつくったりしています・趣味は
constexpr
です
◆⾃⼰紹介•
開催中:– 村上原野
縄⽂⼟器展
『ハロー、縄⽂!』〜2012/3/4
◆⾃⼰紹介•
公開しているライブラリ:
Sprout C++ Library (constexpr
ライブラリ)github.com/bolero-MURAKAMI/Sprout
•
前回発表資料:【中3⼥⼦でもわかる
constexpr】
www.slideshare.net/GenyaMurakami
◆アジェンダ•
はじめに
•
constexpr
おさらい•
constexpr
と
TMP の連携
•
(続)constexpr
レイトレーシング•
まとめ
◆constexpr
とは?•
constexpr
宣⾔された変数は、コンパイル時定数になる
•
constexpr
宣⾔された関数やコンストラクタは、コンパ イル時にも実⾏時にも呼び出すことができる
•
リテラル型のオブジェクトは、コンパイル時定数にでき る
constexpr int always_zero() { return 0; } // constexpr 関数constexpr int compiletime_zero = always_zero(); // コンパイル時呼出int runtime_zero = always_zero(); // 実行時呼出
struct literal_type { }; // リテラル型のクラスconstexpr auto literal = literal_type{ }; // クラスインスタンスを定数式に
constexpr int zero = 0; // constexpr 変数using zero_t = std::integral_constant<int, zero>; // テンプレートにも渡せる
◆constexpr
とは?•
constexpr
関数の制限
–
static_assert, typedef, using, 及び⾼々1つの
return ⽂のみ を書くことができる
–
引数は定数式にならない
template<class T>constexpr T square(T const& n) {
static_assert( true, “” );// static_assert( n != 0, “” );typedef T t1;using t2 = T;using std::ptrdiff_t;using namespace std;return n * n;
}
関数テンプレートもconstexpr
指定できる
◆C++ プログラミングのレイヤー
プリプロセス時の世界(魔界)
コンパイル時の世界(ライブラリアンが多数棲息)
実⾏時の世界(⼈間界)
[プログラマのすること][処理されるもの]
ソースコードプリプロセッサ
メタプログラミング
テンプレートメタプログラミング型
実⾏時オブジェクト
定数式
通常のプログラミング
C++03
◆C++ プログラミングのレイヤー
プリプロセス時の世界(魔界)
コンパイル時の世界(ライブラリアンが多数棲息)
実⾏時の世界(⼈間界)
[プログラマのすること][処理されるもの]
ソースコードプリプロセッサ
メタプログラミング
テンプレートメタプログラミング型
実⾏時オブジェクト
定数式
通常のプログラミング
C++11
constexpr
◆C++ プログラミングのレイヤー
プリプロセス時の世界(魔界)
コンパイル時の世界(ライブラリアンが多数棲息)
実⾏時の世界(⼈間界)
[プログラマのすること][処理されるもの]
constexpr
ソースコードプリプロセッサ
メタプログラミング
テンプレートメタプログラミング型
実⾏時オブジェクト
定数式
通常のプログラミング
C++11
連携したい
◆Sprout C++ Library•
constexpr
⽂字列
•
constexpr
タプル•
constexpr
バリアント
•
constexpr
アルゴリズム•
constexpr
範囲アルゴリズム
•
constexpr
コンテナ操作•
constexpr
乱数
•
constexpr
ハッシュ関数•
constexpr
UUID
•
constexpr
構⽂解析•
constexpr
レイトレーシング
◆Sprout C++ Library•
constexpr
⽂字列
#include <sprout/string.hpp>#include <sprout/algorithm.hpp>
/* コンパイル時文字列 */static constexpr auto hello = sprout::to_string("Hello,world!");static_assert( hello == "Hello,world!", "" );
/* 文字列連結 */static_assert( hello + "!!!" == "Hello,world!!!!", "" );
/* 文字列切り出し */static_assert( hello.substr(0, 5) == "Hello", "" );
/* 文字列反転 */static_assert( sprout::reverse(hello) == "!dlrow,olleH", "" );
◆Sprout C++ Library•
constexpr
⽂字列
このように、constexpr
で様々なクラスをとてもわかりやすく簡単に扱うことができる。
#include <sprout/string.hpp>#include <sprout/algorithm.hpp>
/* コンパイル時文字列 */static constexpr auto hello = sprout::to_string("Hello,world!");static_assert( hello == "Hello,world!", "" );
/* 文字列連結 */static_assert( hello + "!!!" == "Hello,world!!!!", "" );
/* 文字列切り出し */static_assert( hello.substr(0, 5) == "Hello", "" );
/* 文字列反転 */static_assert( sprout::reverse(hello) == "!dlrow,olleH", "" );
◆constexpr
と
TMP の連携•
クラステンプレートにコンパイル時定数
を渡すには•
index_tuple
idiom
•
型⽂字列と
constexpr
⽂字列の相互変換
◆クラステンプレートにコンパイル時定数を渡すには
•
整数型を渡すtemplate<int W, int H> RectArea {
static constexpr int value = W * H;};static constexpr int w = 50;static constexpr int h = 100;static_assert( RectArea<w, h>::value == 5000, “” )
template<int W, int H> RectArea {static constexpr int value = W * H;
};static constexpr int w = 50;static constexpr int h = 100;static_assert( RectArea<w, h>::value == 5000, “” )
◆クラステンプレートにコンパイル時定数を渡すには
•
整数型を渡す 整数型はテンプレート引数にそのまま渡せる
struct Rect { int w; int h; };
template< ??? >struct Area {
static constexpr int value = ???;};
◆クラステンプレートにコンパイル時定数を渡すには
•
整数型以外のオブジェクトを渡すには?
struct Rect { int w; int h; };
template< ??? >struct Area {
static constexpr int value = ???;};
◆クラステンプレートにコンパイル時定数を渡すには
•
整数型以外のオブジェクトを渡すには?
テンプレート引数をどう書けばいい?
このクラスのインスタンスをテンプレートに渡したい
◆クラステンプレートにコンパイル時定数を渡すには
•
クラスを直接テンプレート引数にしてみるtemplate<Rect V>struct Area {
static constexpr int value = V.w * V.h;};
static constexpr Rect rect = {10, 20};static_assert( Area<rect>::value == 200, “” );
◆クラステンプレートにコンパイル時定数を渡すには
•
クラスを直接テンプレート引数にしてみる
•
クラス型をテンプレート引数にすることはできない•
論外
template<Rect V>struct Area {
static constexpr int value = V.w * V.h;};
static constexpr Rect rect = {10, 20};static_assert( Area<rect>::value == 200, “” );
コンパイルエラー!!
◆クラステンプレートにコンパイル時定数を渡すには
•
ポインタをテンプレート引数にしてみるtemplate<Rect const* P>struct Area {
static constexpr int value = P->w * P->h;};
static constexpr Rect rect = {10, 20};static_assert( Area<&rect>::value == 200, “” );
◆クラステンプレートにコンパイル時定数を渡すには
•
ポインタをテンプレート引数にしてみる
•
ポインタ型をテンプレート引数にすることは可能
template<Rect const* P>struct Area {
static constexpr int value = P->w * P->h;};
static constexpr Rect rect = {10, 20};static_assert( Area<&rect>::value == 200, “” );
OK!
◆クラステンプレートにコンパイル時定数を渡すには
•
ポインタをテンプレート引数にしてみるtemplate<Rect const* P>struct Area {
static constexpr int value = P->w * P->h;};
int main() {static constexpr Rect rect = {10, 20};static_assert( Area<&rect>::value == 200, “” );
}
◆クラステンプレートにコンパイル時定数を渡すには
•
ポインタをテンプレート引数にしてみる
•
テンプレート引数に渡せる値–
整数型
/ リンケージを持つオブジェクト
•
ローカル変数、⼀時オブジェクト、⽂字列リテラル等を テンプレート引数に渡すことはできない
template<Rect const* P>struct Area {
static constexpr int value = P->w * P->h;};
int main() {static constexpr Rect rect = {10, 20};static_assert( Area<&rect>::value == 200, “” );
}
コンパイルエラー!!
◆クラステンプレートにコンパイル時定数を渡すには
•
プロキシクラスをテンプレート引数にしてみるtemplate<class Proxy>struct Area {
static constexpr int value = Proxy()().w * Proxy()().h;};
int main() {static constexpr Rect rect = {10, 20};
struct proxy {constexpr Rect const& operator()() const { return rect; }
};static_assert( Area<proxy>::value == 200, “” );
}
◆クラステンプレートにコンパイル時定数を渡すには
•
プロキシクラスをテンプレート引数にしてみる
•
短所–
プロキシクラスをいちいち定義しなければならない
template<class Proxy>struct Area {
static constexpr int value = Proxy()().w * Proxy()().h;};
int main() {static constexpr Rect rect = {10, 20};
struct proxy {constexpr Rect const& operator()() const { return rect; }
};static_assert( Area<proxy>::value == 200, “” );
} OK!
proxy クラスの
operator() はローカル変数の
Rect
を返す
プロキシを介して値を取得
◆クラステンプレートにコンパイル時定数を渡すには
•
ポインタをテンプレート引数にする⽅法
–
リンケージを持つオブジェクト(グローバル変数)しか渡すこと ができない
•
プロキシクラスをテンプレート引数にする⽅法
–
その都度プロキシクラスを作成する必要がある–
(プロキシの定義をマクロにすれば多少書きやすいかも)
template<Object const* P>struct TemplateClass;
template<class Proxy>struct TemplateClass;
◆index_tuple
idiom•
index_tuple
と
index_range
ヘルパ
template<ptrdiff_t... Indexes>struct index_tuple;
template<ptrdiff_t First, ptrdiff_t Last>struct index_range;
static_assert( std::is_same<index_range<0, 5>::type,index_tuple<0, 1, 2, 3, 4>>::value, “” );
◆index_tuple
idiom•
index_tuple
と
index_range
ヘルパ
template<ptrdiff_t... Indexes>struct index_tuple;
template<ptrdiff_t First, ptrdiff_t Last>struct index_range;
static_assert( std::is_same<index_range<0, 5>::type,index_tuple<0, 1, 2, 3, 4>>::value, “” );
パラメータパックをpack expansion expression
(Indexes...) で使う
(First .. Last] のindex_tuple
を⽣成するヘルパメタ関数
◆index_tuple
idiom•
TMP で
index_tuple
を使う
#include <sprout/index_tuple.hpp>#include <boost/mpl/vector.hpp>
template<class Seq, class IndexTuple>struct to_tuple_impl;template<class Seq, ptrdiff_t... Indexes>struct to_tuple_impl<Seq, index_tuple<Indexes...> > {
typedef tuple< mpl::at_c<Seq, Indexes>... > type;};/* MPLシーケンスから
tuple への変換 */template<class Seq>struct to_tuple : to_tuple_impl<
Seq,typename index_range<0, mpl::size<Seq>::value>::type
> { };
typedef mpl::vector<int, double> vec;static_assert( is_same<to_tuple<vec>::type, tuple<int, double> >::value, “” );
#include <sprout/index_tuple.hpp>#include <boost/mpl/vector.hpp>
template<class Seq, class IndexTuple>struct to_tuple_impl;template<class Seq, ptrdiff_t... Indexes>struct to_tuple_impl<Seq, index_tuple<Indexes...> > {
typedef tuple< mpl::at_c<Seq, Indexes>... > type;};/* MPLシーケンスから
tuple への変換 */template<class Seq>struct to_tuple : to_tuple_impl<
Seq,typename index_range<0, mpl::size<Seq>::value>::type
> { };
typedef mpl::vector<int, double> vec;static_assert( is_same<to_tuple<vec>::type, tuple<int, double> >::value, “” );
◆index_tuple
idiom•
TMP で
index_tuple
を使う
index_tuple<Indexes...> でテンプレート引数を特殊化する
pack expansion expression でIndexes を使ったリストに展開する
index_range
で
index_tuple
を⽣成して実装に丸投げ
◆index_tuple
idiom•
constexpr
関数で
index_tuple
を使う
#include <sprout/index_tuple.hpp>#include <sprout/array.hpp>
template<class T, size_t N, ptrdiff_t...Indexes>constexpr array<T, N> to_array_impl(T const (& arr)[N], index_tuple<Indexes...>) {
return array<T, N>{{ arr[Indexes]... }};}/* 生配列から sprout::array への変換 */template<class T, size_t N>constexpr array<T, N> to_array(T const (& arr)[N]) {
return to_array_impl(arr, typename index_range<0, N>::type());}
static constexpr int arr[2] = { 1, 2 };static constexpr array<int, 2> s = to_array(arr);
◆index_tuple
idiom•
constexpr
関数で
index_tuple
を使う
#include <sprout/index_tuple.hpp>#include <sprout/array.hpp>
template<class T, size_t N, ptrdiff_t...Indexes>constexpr array<T, N> to_array_impl(T const (& arr)[N], index_tuple<Indexes...>) {
return array<T, N>{{ arr[Indexes]... }};}/* 生配列から sprout::array への変換 */template<class T, size_t N>constexpr array<T, N> to_array(T const (& arr)[N]) {
return to_array_impl(arr, typename index_range<0, N>::type());}
static constexpr int arr[2] = { 1, 2 };static constexpr array<int, 2> s = to_array(arr);
index_tuple<Indexes...>を引数にして推論させる
pack expansion expression でIndexes を使ったリストに展開する
index_range
で
index_tuple
を⽣成して実装に丸投げ
◆index_tuple
idiom•
index_tuple
イディオムは、インデックスアクセス可能
なデータ構造⼀般に適⽤できる–
配列
–
タプル–
ランダムアクセスイテレータ
–
型リスト
•
constexpr
に限らず、通常の関数や
TMP にも同じよう に応⽤できる
•
ただしインデックスアクセス不可なデータ構造には適⽤ できない
–
⾮ランダムアクセスなイテレータなど
◆型⽂字列と
constexpr
⽂字列の相互変換
•
型⽂字列
–
⽂字列は可変⻑テンプレート引数で表現される–
⽂字列リテラルは使えない
•
(マクロの⿊魔術を使えば制限付きで可能ではある)
•
constexpr
⽂字列
–
コンパイル時定数である–
⽂字列リテラルが使⽤可能
•
これらを相互に変換できるようにしたい
typedef mpl::string< ‘Hell’, ‘o,wo’, ‘rld!’ > hello_t;
static constexpr auto hello = sprout::to_string( “Hello,world!” );
◆型⽂字列と
constexpr
⽂字列の相互変換
•
mpl::string
→
sprout::string
#include <type_traits>#include <sprout/index_tuple.hpp>#include <sprout/type.hpp>#include <sprout/string.hpp>#include <sprout/type/boost/mpl/string.hpp>
using namespace std;using namespace sprout;namespace mpl = boost::mpl;namespace types = sprout::types;
◆型⽂字列と
constexpr
⽂字列の相互変換
•
mpl::string
→
sprout::string/* T::value がナル文字であるか返すメタ関数クラス
*/
struct is_nul {template<class T, class = void>struct apply : false_type { };template<class T>struct apply<T, typename enable_if<T::value == 0>::type>
: true_type{ };
};/* ナル文字までの文字列長 */template<class Seq>struct str_length : types::distance<
typename types::begin<Seq>::type,typename types::seq::find_if<Seq, is_nul>::type
> { };
◆型⽂字列と
constexpr
⽂字列の相互変換
•
mpl::string
→
sprout::stringtemplate<class Seq, ptrdiff_t... Indexes>constexpr sprout::basic_string<
typename Seq::value_type,str_length<Seq>::value
> to_sprout_string_impl( index_tuple<Indexes...> ) {return sprout::make_string_as<typename Seq::value_type>(
types::tuple_element<Indexes, Seq>::type::value...);
}/* sprout::string に変換 */template<class Seq>constexpr sprout::basic_string<
typename Seq::value_type,str_length<Seq>::value
> to_sprout_string() {return to_sprout_string_impl<Seq>(
typename index_range<0, str_length<Seq>::value>::type());
}
◆型⽂字列と
constexpr
⽂字列の相互変換
•
mpl::string
→
sprout::stringtemplate<class Seq, ptrdiff_t... Indexes>constexpr sprout::basic_string<
typename Seq::value_type,str_length<Seq>::value
> to_sprout_string_impl( index_tuple<Indexes...> ) {return sprout::make_string_as<typename Seq::value_type>(
types::tuple_element<Indexes, Seq>::type::value...);
}/* sprout::string に変換 */template<class Seq>constexpr sprout::basic_string<
typename Seq::value_type,str_length<Seq>::value
> to_sprout_string() {return to_sprout_string_impl<Seq>(
typename index_range<0, str_length<Seq>::value>::type());
}
Indexes
を使ってシーケンス要素の⽂字のリストに展開
index_tuple<Indexes...> を実装関数で受け取る
basic_string<Elem, Length>
index_range
で
index_tuple
を⽣成して実装に丸投げ
◆型⽂字列と
constexpr
⽂字列の相互変換
•
mpl::string
→
sprout::string
•
⼀般に【型→値】の変換は⾃明に書ける(場合が多い)
typedef mpl::string< ‘foo’, ‘bar’ > type;static constexpr auto s = to_sprout_string<type>();static_assert( s == “foobar”, “” );
mpl::string
(型)からsprout::string
(コンパイル時定数)へ変換
◆型⽂字列と
constexpr
⽂字列の相互変換
•
sprout::string
→
mpl::string
#include <type_traits>#include <sprout/index_tuple.hpp>#include <sprout/fixed_container.hpp>#include <sprout/string.hpp>#include <boost/mpl/string.hpp>#include <boost/preprocessor/cat.hpp>
using namespace std;using namespace sprout;namespace mpl = boost::mpl;
◆型⽂字列と
constexpr
⽂字列の相互変換
•
sprout::string
→
mpl::stringtemplate<class Proxy>struct to_mpl_string {
template<class IndexTuple>struct impl;template<ptrdiff_t... Indexes>struct impl<index_tuple<Indexes...> > {
typedef mpl::string<sprout::begin(Proxy()())[Indexes]...
> type;};/* mpl::string に変換 */typedef class impl<
typename index_range<0, sprout::size(Proxy()())>::type>::type type;
};
◆型⽂字列と
constexpr
⽂字列の相互変換
•
sprout::string
→
mpl::stringtemplate<class Proxy>struct to_mpl_string {
template<class IndexTuple>struct impl;template<ptrdiff_t... Indexes>struct impl<index_tuple<Indexes...> > {
typedef mpl::string<sprout::begin(Proxy()())[Indexes]...
> type;};/* mpl::string に変換 */typedef class impl<
typename index_range<0, sprout::size(Proxy()())>::type>::type type;
};
Indexes
を使ってシーケンス要素の⽂字のリストに展開
index_tuple<Indexes...> を実装メタ関数で受け取る
mpl::string<Elem...>
index_range
で
index_tuple
を⽣成して実装に丸投げ
◆型⽂字列と
constexpr
⽂字列の相互変換
•
sprout::string
→
mpl::string
/* 変換結果を typedef するマクロ*/#define STRING_CLASS_TYPEDEF(SOURCE, TYPE) ¥struct BOOST_PP_CAT(PROXY_, __LINE__) { ¥
constexpr typename remove_reference<decltype(SOURCE)>::type ¥operator() () const { return SOURCE; } ¥
}; ¥typedef typename to_mpl_string< ¥
BOOST_PP_CAT(PROXY_, __LINE__) ¥>::type TYPE
◆型⽂字列と
constexpr
⽂字列の相互変換
•
sprout::string
→
mpl::string
/* 変換結果を typedef するマクロ*/#define STRING_CLASS_TYPEDEF(SOURCE, TYPE) ¥struct BOOST_PP_CAT(PROXY_, __LINE__) { ¥
constexpr typename remove_reference<decltype(SOURCE)>::type ¥operator() () const { return SOURCE; } ¥
}; ¥typedef typename to_mpl_string< ¥
BOOST_PP_CAT(PROXY_, __LINE__) ¥>::type TYPE
PROXY_127 のような名前のプロキシクラスが定義される
operator() は変換元ソースをそのまま返す
constexpr
関数
定義したプロキシクラスを渡す
◆型⽂字列と
constexpr
⽂字列の相互変換
•
sprout::string
→
mpl::string
•
【値→型】の変換は⼯夫次第で可能•
⽋点–
⾃明な書き⽅ができない(場合が多い)
–
プロキシクラスの定義が必要な場合、インラインに書けないし、 constexpr
関数の中で使えない
#include <boost/mpl/equal.hpp>
static constexpr auto s = sprout::to_string( “foobar” );STRING_CLASS_TYPEDEF(s, type);static_assert( mpl::equal< type, mpl::string<'foo', 'bar'> >::value, “” );
sprout::string
(コンパイル時定数)からmpl::string
(型)へ変換
◆constexpr
と
TMP の連携•
整数型以外のコンパイル時定数でも、テンプ
レートに受け渡す⽅法はある
•
index_tuple
イディオムや、プロキシクラス等 を活⽤することで、constexpr
と
TMP の間で
相互にやりとりをすることができる
◆(続)constexpr
レイトレーシング• Sprout.Darkroom
–
metatrace
という
TMP ライブラリを元にした
constexpr
レイ トレーサーライブラリ
◆(続)constexpr
レイトレーシング• 2つの球(Boost.勉強会#7 東京時点)
◆(続)constexpr
レイトレーシング• 鏡の部屋の2つの球
◆(続)constexpr
レイトレーシング• 鏡の部屋の2つの球(市松模様の床)
◆(続)constexpr
レイトレーシング• 地球のような何か
◆(続)constexpr
レイトレーシング• エレメント(カラーやスペキュラ)を表現するには
template<typename Element, typename Scale>class plaid_element {private:
Element elem1_;Element elem2_;Scale scale_;template<typename Unit>constexpr Element calc_1(Unit const& u, Unit const& v) const {
return (u >= 0 && v >= 0) || (u < 0 && v < 0)? elem1_ : elem2_;
}public:
template<typename Unit>constexpr Element operator() (Unit const& u, Unit const& v) const {
return calc_1((u < 0 ? scale_ + fmod(u, scale_) : fmod(u, scale_)) - scale_ / 2,(v < 0 ? scale_ + fmod(v, scale_) : fmod(v, scale_)) - scale_ / 2);
}};
◆(続)constexpr
レイトレーシング• エレメント(カラーやスペキュラ)を表現するには
template<typename Element, typename Scale>class plaid_element {private:
Element elem1_;Element elem2_;Scale scale_;template<typename Unit>constexpr Element calc_1(Unit const& u, Unit const& v) const {
return (u >= 0 && v >= 0) || (u < 0 && v < 0)? elem1_ : elem2_;
}public:
template<typename Unit>constexpr Element operator() (Unit const& u, Unit const& v) const {
return calc_1((u < 0 ? scale_ + fmod(u, scale_) : fmod(u, scale_)) - scale_ / 2,(v < 0 ? scale_ + fmod(v, scale_) : fmod(v, scale_)) - scale_ / 2);
}};
市松模様を表現するエレメント
座標を⼆つに分けてどちらかのエレメント(⽩/⿊など)を返す
エレメントのコンセプトはuv
座標を受け取って結果を返す operator()(u, v) を持つこと
◆(続)constexpr
レイトレーシング• テクスチャマップを読み込むには
– earth.png
#include <sprout/darkroom.hpp>
/* ファイル
"earth.tex.hpp" の内容を tex に読み込む */
#define DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER tex#define DARKROOM_DEF_LOAD_TEXTURE_FILE "earth.tex.hpp"#include DARKROOM_LOAD_TEXTURE
◆(続)constexpr
レイトレーシング• テクスチャマップを読み込むには
– 擬似コード
#include <sprout/darkroom.hpp>
/* ファイル
"earth.tex.hpp" の内容を tex に読み込む */
#define DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER tex#define DARKROOM_DEF_LOAD_TEXTURE_FILE "earth.tex.hpp"#include DARKROOM_LOAD_TEXTURE
/* 実装は include ディレクティブで
CVS を取りこむトリックと同じような感じ
*/
constexpr auto tex {# include "earth.tex.hpp"};
“earth.tex.hpp”
の中⾝はコンマ区切りのピクセルデータのようなもの
◆まとめ•
constexpr
を活⽤することで、『型』
『オブジェクト』『外部データ』等さま ざまなソースを、コンパイル時に相互に 扱うことができる
•
constexpr
でもっともっと遊ぼう!!
ご清聴ご清聴 ありがとうありがとう ございましたございました
Recommended