コンパイル時の if ステートメント if constexpr
構文 を使用するとif constexpr(...)
、コンパイラはコンパイル時の条件式を評価してif
、コンパイル時にステートメントのthen部分とelse部分のどちらを使用するかを決定できます。コードの残りの部分は破棄されます。つまり、コードは生成されません。ただし、これは破棄された部分が完全に無視されるという意味ではなく、これらの部分のコードも未使用のテンプレートなどの構文がチェックされます。
例えば:
#include <string>
template <typename T>
std::string asString(T x)
{
if constexpr(std::is_same_v<T, std::string>) {
return x; // 如果T不能自动转换为string该语句将无效
}
else if constexpr(std::is_arithmetic_v<T>) {
return std::to_string(x); // 如果T不是数字类型该语句将无效
}
else {
return std::string(x); // 如果不能转换为string该语句将无效
}
}
を使用すると、渡された文字列を単に返すか、渡された数値を呼び出すか、コンストラクターを使用して渡されたパラメータを に変換するかif constexpr
をコンパイル時に決定できます。無効な呼び出しは破棄されるため、次のコードはコンパイルされます (ランタイム ステートメントを使用する場合はコンパイルされません)。to_string()
std::string
if
#include <iostream>
int main()
{
std::cout << asString(42) << '\n';
std::cout << asString(std::string("hello")) << '\n';
std::cout << asString("hello") << '\n';
}
プリコンパイルされたコードは次のとおりです。
//代码std::cout << asString(42) << '\n';的预编译代码如下:
/* First instantiated from: insights.cpp:19 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string<char, std::char_traits<char>, std::allocator<char> > asString<int>(int x)
{
if constexpr(false) {
} else /* constexpr */ {
if constexpr(true) {
return std::to_string(x);
}
}
}
#endif
//代码std::cout << asString(std::string("hello")) << '\n';的预编译代码如下:
/* First instantiated from: insights.cpp:20 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string<char, std::char_traits<char>, std::allocator<char> > asString<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > x)
{
if constexpr(true) {
return std::basic_string<char, std::char_traits<char>, std::allocator<char> >(static_cast<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &&>(x));
}
}
#endif
//代码std::cout << asString("hello") << '\n';的预编译代码如下:
/* First instantiated from: insights.cpp:21 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string<char, std::char_traits<char>, std::allocator<char> > asString<const char *>(const char * x)
{
if constexpr(false) {
} else /* constexpr */ {
if constexpr(false) {
} else /* constexpr */ {
return std::basic_string<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >(x, std::allocator<char>()));
}
}
}
#endif
int main()
{
std::operator<<(std::operator<<(std::cout, asString(42)), '\n');
std::operator<<(std::operator<<(std::cout, asString(std::basic_string<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >("hello", std::allocator<char>())))), '\n');
std::operator<<(std::operator<<(std::cout, asString("hello")), '\n');
return 0;
}
コンパイル時if
ステートメント
上記の例でランタイムを使用したif
場合、次のコードはコンパイルできません。
#include <string>
template <typename T>
std::string asString(T x)
{
if (std::is_same_v<T, std::string>) {
return x; // 如果不能自动转换为string会导致ERROR
}
else if (std::is_numeric_v<T>) {
return std::to_string(x); // 如果不是数字将导致ERROR
}
else {
return std::string(x); // 如果不能转换为string将导致ERROR
}
}
これは、テンプレートがインスタンス化されるときに全体としてコンパイルされるためです。ただし、if
ステートメントの条件式のチェックは実行時機能です。コンパイル時に条件式の値が でなければならないと判断できる場合でもfalse
、then部分もコンパイルに合格できなければなりません。したがって、std::string
または 文字列リテラルを渡すと、std::to_string()
無効であるためコンパイルに失敗します。また、数値を渡す場合、最初と 3 番目の return ステートメントが無効であるため、コンパイルは失敗します。
コンパイル時ステートメントを使用する場合if
、使用できないthenおよびelse部分は破棄されたステートメントになります。
- one を渡すと、
std::string
最初のif
ステートメントのelse部分は破棄されます。
template<>
std::basic_string<char, std::char_traits<char>, std::allocator<char> > asString<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > x)
{
if constexpr(true) {
return std::basic_string<char, std::char_traits<char>, std::allocator<char> >(static_cast<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &&>(x));
}
}
- 数値を渡す場合、
if
最初のステートメントのthen部分と最後のelse部分は破棄されます。
#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string<char, std::char_traits<char>, std::allocator<char> > asString<int>(int x)
{
if constexpr(false) {
} else /* constexpr */ {
if constexpr(true) {
return std::to_string(x);
}
}
}
#endif
- 文字列リテラル (型 ) を渡す場合、
const char*
最初と 2 番目のif
ステートメントのthen部分は破棄されます。
#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string<char, std::char_traits<char>, std::allocator<char> > asString<const char *>(const char * x)
{
if constexpr(false) {
} else /* constexpr */ {
if constexpr(false) {
} else /* constexpr */ {
return std::basic_string<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >(x, std::allocator<char>()));
}
}
}
#endif
したがって、インスタンス化のたびに無効なブランチがコンパイル時に破棄されるため、コードは正常にコンパイルされます。削除されたステートメントは無視されないことに注意してください。無視されるステートメントであっても正しい構文に準拠する必要があり、テンプレート パラメーターに関連しないすべての呼び出しも正しい必要があります。実際、テンプレートのコンパイルの最初のフェーズ (定義中) では、すべてのテンプレートに依存しない名前の構文と有効性がチェックされます。static_asserts
ブランチがコンパイルされていない場合でも、すべてが有効である必要があります。
例えば:
template<typename T>
void foo(T t)
{
if constexpr(std::is_integral_v<T>) {
if (t > 0) {
foo(t-1); // OK
}
}
else {
undeclared(t); // 如果未被声明且未被丢弃将导致错误
undeclared(); // 如果未声明将导致错误(即使被丢弃也一样)
static_assert(false, "no integral"); // 总是会进行断言(即使被丢弃也一样)
}
}
標準に準拠したコンパイラの場合、上記の例はコンパイルできず、次のようなエラーが発生します。
<source>:17:9: error: there are no arguments to 'undeclared' that depend on a template parameter, so a declaration of 'undeclared' must be available [-fpermissive]
17 | undeclared(); // 如果未声明将导致错误(即使被丢弃也一样)
| ^~~~~~~~~~
<source>:17:9: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
<source>:18:23: error: static assertion failed: no integral
18 | static_assert(false, "no integral"); // 总是会进行断言(即使被丢弃也一样)
理由は 2 つあります。
T
整数型でも、次のように呼び出されます。
undeclared(); // 如果未声明将导致错误(即使被丢弃也一样)
関数が未定義の場合、破棄されたelse部分にある場合でも、呼び出しはテンプレートパラメータに依存しないため、エラーが発生します。
- 次のようにアサートします
static_assert(false, "no integral"); // 总是会进行断言(即使被丢弃也一样)
破棄されたelse部分でさえ、テンプレート パラメーターにも依存しないため、アサーションは常に失敗します。コンパイル時の条件を使用する静的アサーションには、この問題はありません。
static_assert(!std::is_integral_v<T>, "no integral");
一部のコンパイラ (例:Visual C++ 2013
および2015
) は、テンプレート コンパイルの 2 つのフェーズを正しく実装していないことに注意してください。ほとんどの作業が第 1 フェーズ (定義中) から第 2 フェーズ (インスタンス化中)に延期されるため、一部の無効な関数呼び出しや一部の不正な構文がコンパイルされる可能性があります。
コンパイル時if
ステートメントを使用する
理論的に言えば、条件式がコンパイル時式である限り、if
コンパイル時を runtime として使用できますif
。コンパイル時と実行時を混在させることもできますif
。
if constexpr (std::is_integral_v<std::remove_reference_t<T>>) {
if (val > 10) {
if constexpr (std::numeric_limits<char>::is_signed) {
...
}
else {
...
}
}
else {
...
}
}
else {
...
}
関数本体の外では使用できないことに注意してくださいif constexpr
。したがって、これをプリプロセッサの条件付きコンパイルの代わりに使用することはできません。
コンパイル時のif
注意事項
コンパイル時if
に使用すると、明らかではない結果が生じる可能性があります。
コンパイルはif
戻り値の型に影響します
関数の戻り値の型はコンパイル時に影響を受けるif
可能性があります。たとえば、次のコードは常にコンパイルされますが、戻り値の型は異なる場合があります。
auto foo()
{
if constexpr (sizeof(int) > 4) {
return 42;
}
else {
return 42u;
}
}
ここでは を使用しているためauto
、戻り値の型は return ステートメントによって異なり、どの return ステートメントが実行されるかはint
バイト数によって異なります。
- 4バイトより大きい場合は返された
42
return文が有効となるため、戻り値の型は となりますint
。 - それ以外の場合は、返された
42u
return ステートメントが有効になるため、戻り値の型は になりますunsigned int
。
前処理コードは次のとおりです。
unsigned int foo()
{
if constexpr(false) {
return 42;
} else /* constexpr */ {
return 42U;
}
}
この場合、if constexpr
ステートメントを含む関数はまったく異なる型を返す可能性があります。たとえば、 else部分を記述しない場合、戻り値はint
次のいずれかになりますvoid
。
auto foo() // 返回值类型可能是int或者void
{
if constexpr (sizeof(int) > 4) {
return 42;
}
}
前処理コードは次のとおりです。
void foo()
{
if constexpr(false) {
return 42;
}
}
//或者
int foo()
{
if constexpr(true) {
return 42;
}
}
ここでランタイムが使用される場合if
、コードは決してコンパイルされないことに注意してください。戻り値の型を導出するときに、考えられるすべての戻り値の型が考慮されるため、導出は曖昧になります。
例えば
#include <iostream>
#include <string>
#include <utility>
using namespace std;
auto foo() {
if (sizeof(int) > 4) {
return 42;
} else {
return 42u;
}
}
int main() {
return 0; }
次のエラーが発生します。
<source>:13:16: error: inconsistent deduction for auto return type: 'int' and then 'unsigned int'
13 | return 42u;
| ^~~
then部分でreturn してもelse部分を考慮します
if
コンパイル時には適用されないパターンが実行時にありますif
。コードがthen 部分とelse部分の両方で返る場合、if
実行時else
その部分をスキップできます。つまり、
if (...) {
return a;
}
else {
return b;
}
次のように書くことができます:
if (...) {
return a;
}
return b;
ただし、このパターンはコンパイル時に適用できませんif
。2 番目の方法では、戻り値の型がどちらかの return ステートメントではなく両方の return ステートメントに依存し、動作の変更につながるためです。たとえば、上記の例に従ってコードを変更した場合、コンパイルできる場合とできない場合があります。
auto foo()
{
if constexpr (sizeof(int) > 4) {
return 42;
}
return 42u;
}
条件式が true ( int
4 バイトを超える) の場合、コンパイラは2 つの異なる戻り値の型を推定し、エラーが発生します。それ以外の場合、有効な return ステートメントは 1 つだけになるため、コードはコンパイルされます。
考えられるエラーは次のとおりです。
<source>:11:12: error: inconsistent deduction for auto return type: 'int' and then 'unsigned int'
11 | return 42u;
| ^~~
コンパイル時のショート評価
次のコードを考えてみましょう。
template<typename T>
constexpr auto foo(const T& val)
{
if constexpr(std::is_integral<T>::value) {
if constexpr (T{
} < 10) {
return val * 2;
}
}
return val;
}
ここでは、2 つのコンパイル時条件を使用して、渡された値を直接返すか、渡された値の 2 倍を返すかを決定します。
次のコードはすべてコンパイルされます。
constexpr auto x1 = foo(42); // 返回84
constexpr auto x2 = foo("hi"); // OK,返回"hi"
前処理コードは次のとおりです。
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr int foo<int>(const int & val)
{
if constexpr(true) {
if constexpr(true) {
return val * 2;
}
}
return val;
}
#endif
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr const char * foo<char[3]>(const char (&val)[3])
{
if constexpr(false) {
}
return val;
}
#endif
int main()
{
constexpr const int x1 = foo(42);
constexpr const char *const x2 = foo("hi");
return 0;
}
実行時の条件式はif
、短絡評価を行います (&&
左辺がfalse
の場合は評価を停止し、||
左辺がtrue
の場合は評価を停止します)。これにより、if
コンパイル時に期待される評価が短絡される可能性もあります。
template<typename T>
constexpr auto bar(const T& val)
{
if constexpr (std::is_integral<T>::value && T{
} < 10) {
return val * 2;
}
return val;
}
ただし、コンパイル時のif
条件式は常に全体としてインスタンス化され、全体として有効である必要があります。つまり、<10
評価できない型を渡すとコンパイルに失敗します。
constexpr auto x2 = bar("hi"); // 编译期ERROR
エラーメッセージは次のとおりです。
<source>: In instantiation of 'constexpr auto bar(const T&) [with T = char [3]]':
<source>:17:28: required from here
<source>:10:53: error: taking address of temporary array
10 | if constexpr (std::is_integral<T>::value && T{
} < 10) {
| ~~~~^~~~
<source>:11:20: error: invalid operands of types 'const char [3]' and 'int' to binary 'operator*'
11 | return val * 2;
| ~~~~^~~
<source>: In function 'int main()':
<source>:17:28: error: 'constexpr auto bar(const T&) [with T = char [3]]' called in a constant expression
17 | constexpr auto x2 = bar("hi");
| ~~~^~~~~~
<source>:8:16: note: 'constexpr auto bar(const T&) [with T = char [3]]' is not usable as a 'constexpr' function because:
8 | constexpr auto bar(const T& val)
|
したがって、コンパイル時はインスタンス化時if
の評価を短縮しません。後者の条件の有効性が前の条件に依存する場合は、条件をネストする必要があります。たとえば、次のように記述する必要があります。
if constexpr (std::is_same_v<MyType, T>) {
if constepxr (T::i == 42) {
...
}
}
書く代わりに:
if constexpr (std::is_same_v<MyType, T> && T::i == 42) {
...
}
その他のコンパイル時間if
の例
完全な戻り値のジェネリック値
if
1 つのアプリケーションでは、コンパイル時に最初に戻り値に対して何らかの処理を実行し、次に完全転送を実行します。(不完全な型であるため)decltype(auto)
推定できないため、次のように記述する必要があります。void
void
#include <functional> // for std::forward()
#include <functional> // for std::forward()
#include <iostream>
#include <type_traits> // for std::is_same<> and std::invoke_result<>
using namespace std;
double foo1(int x, int y) {
return x * 2.5 + y; }
void foo2() {
cout << "foo2" << endl; }
template <typename Callable, typename... Args>
decltype(auto) call(Callable op, Args&&... args) {
if constexpr (std::is_void_v<std::invoke_result_t<Callable, Args...>>) {
// 返回值类型是void:
op(std::forward<Args>(args)...);
return;
} else {
// 返回值类型不是void:
decltype(auto) ret{
op(std::forward<Args>(args)...)};
return ret;
}
}
int main() {
double tv1 = call(foo1, 5, 3);
cout << tv1 << endl;
call(foo2);
return 0;
}
関数の戻り値の型は として推定できますvoid
が、ret
の宣言は として推定できないvoid
ため、op
戻りvoid
値の場合は個別に処理する必要があります。
操作の結果は次のようになります。
15.5
foo2
プリコンパイルされたコードは次のとおりです。
#include <functional> // for std::forward()
#include <iostream>
#include <type_traits> // for std::is_same<> and std::invoke_result<>
using namespace std;
double foo1(int x, int y)
{
return (static_cast<double>(x) * 2.5) + static_cast<double>(y);
}
void foo2()
{
std::operator<<(std::cout, "foo2").operator<<(std::endl);
}
template<typename Callable, typename ... Args>
decltype(auto) call(Callable op, Args &&... args)
{
if constexpr(std::is_void_v<std::invoke_result_t<Callable, Args...> >) {
op(std::forward<Args>(args)... );
return;
} else /* constexpr */ {
decltype(auto) ret = {
op(std::forward<Args>(args)... )};
return ret;
}
}
#ifdef INSIGHTS_USE_TEMPLATE
template<>
double call<double (*)(int, int), int, int>(double (*op)(int, int), int && __args1, int && __args2)
{
if constexpr(false) {
} else /* constexpr */ {
double ret = {
op(std::forward<int>(__args1), std::forward<int>(__args2))};
return ret;
}
}
#endif
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void call<void (*)()>(void (*op)())
{
if constexpr(true) {
op();
return;
}
}
#endif
int main()
{
double tv1 = call(foo1, 5, 3);
std::cout.operator<<(tv1).operator<<(std::endl);
call(foo2);
return 0;
}
コンパイル時にif
型をディスパッチする
コンパイル時if
の一般的な使用法は、型dispatchです。が登場する前はC++17
、処理したい型ごとに個別の関数をオーバーライドする必要がありました。今では、コンパイル時にif
すべてのロジックを関数に入れることができます。
たとえば、次のstd::advance()
アルゴリズムのオーバーロードされたバージョンは次のとおりです。
template<typename Iterator, typename Distance>
void advance(Iterator& pos, Distance n) {
using cat = std::iterator_traits<Iterator>::iterator_category;
advanceImpl(pos, n, cat{
}); // 根据迭代器类型进行分发
}
template<typename Iterator, typename Distance>
void advanceImpl(Iterator& pos, Distance n, std::random_access_iterator_tag) {
pos += n;
}
template<typename Iterator, typename Distance>
void advanceImpl(Iterator& pos, Distance n, std::bidirectional_iterator_tag) {
if (n >= 0) {
while (n--) {
++pos;
}
}
else {
while (n++) {
--pos;
}
}
}
template<typename Iterator, typename Distance>
void advanceImpl(Iterator& pos, Distance n, std::input_iterator_tag) {
while (n--) {
++pos;
}
}
すべての実装を同じ関数に入れることができるようになりました。
template<typename Iterator, typename Distance>
void advance(Iterator& pos, Distance n) {
using cat = std::iterator_traits<Iterator>::iterator_category;
if constexpr (std::is_convertible_v<cat, std::random_access_iterator_tag>) {
pos += n;
}
else if constexpr (std::is_convertible_v<cat, std::bidirectional_access_iterator_tag>) {
if (n >= 0) {
while (n--) {
++pos;
}
}
else {
while (n++) {
--pos;
}
}
}
else {
// input_iterator_tag
while (n--) {
++pos;
}
}
}
ここではコンパイル期間があるようでswitch
、各if constexpr
ステートメントは 1 つのもののようですcase
。ただし、この例の 2 つの実装には 1 つの違いがあることに注意してください。
- オーバーロードされた関数のバージョンは、最も一致するセマンティクスに従います。
- コンパイル時のバージョンは、最初に一致する
if
セマンティクスに従います。
型ディスパッチのもう 1 つの例は、コンパイル時のif
実装get<>()
オーバーロードを使用して構造化バインディング インターフェイスを実装することです。
3 番目の例は、std::variant<>
アクセサーとして使用されるジェネリックlambda
のさまざまな型を扱うことです。
初期化を伴うコンパイル時if
ステートメント
コンパイル時のif
ステートメントでは、新しい初期化された形式も使用できることに注意してください。たとえば、constexpr
function がある場合はfoo()
、次のように記述できます。
#include <functional> // for std::forward()
#include <iostream>
#include <type_traits> // for std::is_same<> and std::invoke_result<>
using namespace std;
double foo(int x) {
return x * 2.5; }
template <typename T>
void bar(const T x) {
if constexpr (auto obj = foo(x); std::is_same_v<decltype(obj), T>) {
std::cout << "foo(x) yields same type\n";
} else {
std::cout << "foo(x) yields different type\n";
}
}
int main() {
bar(2);
bar(2.5);
return 0;
}
操作の結果は次のようになります。
foo(x) yields different type
foo(x) yields same type
前処理コードは次のとおりです。
#include <functional> // for std::forward()
#include <iostream>
#include <type_traits> // for std::is_same<> and std::invoke_result<>
using namespace std;
double foo(int x)
{
return static_cast<double>(x) * 2.5;
}
template<typename T>
void bar(const T x)
{
{
auto obj = foo(x);
if constexpr(std::is_same_v<decltype(obj), T>) {
std::operator<<(std::cout, "foo(x) yields same type\n");
} else /* constexpr */ {
std::operator<<(std::cout, "foo(x) yields different type\n");
}
}
}
/* First instantiated from: insights.cpp:20 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void bar<int>(const int x)
{
{
double obj = foo(x);
if constexpr(false) {
} else /* constexpr */ {
std::operator<<(std::cout, "foo(x) yields different type\n");
}
}
}
#endif
/* First instantiated from: insights.cpp:21 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void bar<double>(const double x)
{
{
double obj = foo(static_cast<int>(x));
if constexpr(true) {
std::operator<<(std::cout, "foo(x) yields same type\n");
}
}
}
#endif
int main()
{
bar(2);
bar(2.5);
return 0;
}
パラメータが type である関数がある場合T
、それが同じ型を返すかどうかに応じて、異なる方法で処理できます。戻り値に基づいて判断したい場合は、次のように記述できます。constexpr
foo()
foo(x)
x
foo(x)
#include <functional> // for std::forward()
#include <iostream>
#include <type_traits> // for std::is_same<> and std::invoke_result<>
using namespace std;
constexpr int foo(int x) {
return x * 2; }
void bar() {
constexpr auto c = 2;
if constexpr (constexpr auto obj = foo(c); obj == 4) {
std::cout << "foo("<<"c"<<") == " << obj << endl;
} else {
std::cout << "foo(x) yields different type\n";
}
}
int main() {
bar();
return 0;
}
条件文で値を使用する場合はobj
、obj
それを として宣言する必要があることに注意してくださいconstexpr
。操作の結果は次のようになります。
foo(c) == 4
前処理コードは次のとおりです。
#include <functional> // for std::forward()
#include <iostream>
#include <type_traits> // for std::is_same<> and std::invoke_result<>
using namespace std;
inline constexpr int foo(int x)
{
return x * 2;
}
void bar()
{
constexpr const int c = 2;
{
constexpr const int obj = foo(c);
if constexpr(true) {
std::operator<<(std::operator<<(std::operator<<(std::cout, "foo("), "c"), ") == ").operator<<(obj).operator<<(std::endl);
} else /* constexpr */ {
std::operator<<(std::cout, "foo(x) yields different type\n");
}
}
}
int main()
{
bar();
return 0;
}
テンプレートの外でコンパイル時間を使用するif
if constexpr
テンプレートだけでなく、あらゆる関数で使用できます。bool
条件式がコンパイル時に型に変換可能である限り。ただし、 then 部分とelse部分のすべてのステートメントは、破棄されてもよい場合でも、通常の関数で使用する場合には有効でなければなりません。
たとえば、次のコードは、符号付きの値によってelse部分が破棄される場合undeclared()
でも、 への呼び出しが有効である必要があるため、コンパイルされません。char
#include <limits>
template<typename T>
void foo(T t);
int main()
{
if constexpr(std::numeric_limits<char>::is_signed) {
foo(42); // OK
}
else {
undeclared(42); // 未声明时总是ERROR(即使被丢弃)
}
}
次のコードも、失敗する静的アサーションが常に存在するため、正常にコンパイルされることはありません。
if constexpr(std::numeric_limits<char>::is_signed) {
static_assert(std::numeric_limits<char>::is_signed);
}
else {
static_assert(!std::numeric_limits<char>::is_signed);
}
汎用コードの外側でコンパイル時に使用するif
唯一の利点は、破棄された部分が最終プログラムの一部にならず、結果として得られる実行可能プログラムのサイズが小さくなることです。たとえば、次のようなプログラムです。
#include <limits>
#include <string>
#include <array>
int main()
{
if (!std::numeric_limits<char>::is_signed) {
static std::array<std::string, 1000> arr1;
...
}
else {
static std::array<std::string, 1000> arr2;
...
}
}
どちらかが最終的な実行可能ファイルarr1
の一部になりますarr2
が、両方が含まれるわけではありません。