質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

853閲覧

C++20 で丁度いいサイズの変数を得たい

bjnes

総合スコア113

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/06/22 06:48

前提・実現したいこと

C++20
テンプレート引数に与えられた整数範囲に基づいて変数の型を変えたい

具体的には、
0UINT8_MAX -> uint8_t
UINT8_MAX
UINT16_MAX -> uint16_t
UINT16_MAX  〜UINT32_MAX → uint32_t

となるようなテンプレートの書き方を教えていただけないでしょうか。

該当のソースコード

具体的には以下のようなソースコードを通したいです。
auto??? の部分が最終的にはtemplate <uint64_t Range>から推測されるものになると思います。

C++

1template <uint64_t Range> 2class FitSizeVariable 3{ 4 auto??? Variable; 5} 6 7TEST() 8{ 9 FitSizeVariable<UINT8_MAX> val1 = FitSizeVariable<UINT8_MAX>(); 10 FitSizeVariable<UINT16_MAX> val2 = FitSizeVariable<UINT16_MAX>(); 11 FitSizeVariable<UINT32_MAX> val3 = FitSizeVariable<UINT32_MAX>(); 12 13 ASSERT_TRUE(sizeof( val1) == sizeof(uint8_t)) 14 15 ASSERT_TRUE(sizeof( val2) == sizeof(uint16_t)) 16 17 ASSERT_TRUE(sizeof( val3) == sizeof(uint32_t)) 18 19} 20 21 22

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2020/06/22 06:59 編集

数値じゃなくてそのままuint8_tなど型を指定するのではダメなんですか? (ダメだから聞いているんだとは思いますが、たまに「よく考えたら必要なかった」という質問ってあるので、念の為)
bjnes

2020/06/22 07:23

とある数値からその数値が格納できる最低限の整数型を確保したいのです。 (コンパイル時に) 具体的には空きメモリー領域を十分に指すことのできる”最低限”の整数型がほしいです。組み込みで利用するので極論1バイトでも短縮させたいのです。 リンカーで空きメモリ領域を調べて、その定数が例えば0xAAAA0000〜0xAAAAFFFFが空きだとしたときは0xFFFF分の2バイトだけ変数化したいのです。空きメモリ量は、コンパイルするたびに変わってしまいますから、 数値から必要なサイズを割り出したく思ってます。
guest

回答2

0

ベストアンサー

パターンがみっつしかありませんから、各場合を部分特殊化する形にするのが手っ取り早い方法だと思います。 これなら C++11 の範囲で書けます。

cpp

1#include <cassert> 2#include <cstdint> 3#include <type_traits> 4 5template<std::size_t r, class T=void> 6class FitSizeVariable { 7}; 8 9template<std::size_t r> 10struct FitSizeVariable<r, typename std::enable_if<r <= UINT8_MAX>::type> { 11 using type = uint8_t; 12}; 13 14template<std::size_t r> 15struct FitSizeVariable<r, typename std::enable_if<(r<=UINT16_MAX) && (r>UINT8_MAX)>::type> { 16 using type = uint16_t; 17}; 18 19template<std::size_t r> 20struct FitSizeVariable<r, typename std::enable_if<(r<= UINT32_MAX) && (r>UINT16_MAX)>::type> { 21 using type = uint32_t; 22}; 23 24template<std::size_t r> 25using FitSizeVariable_t = typename FitSizeVariable<r>::type; 26 27/// テストケース 28int main(void) { 29 FitSizeVariable_t<UINT8_MAX> val1{}; 30 FitSizeVariable_t<UINT16_MAX> val2{}; 31 FitSizeVariable_t<UINT32_MAX> val3{}; 32 33 assert(sizeof(val1) == sizeof(uint8_t)); 34 assert(sizeof(val2) == sizeof(uint16_t)); 35 assert(sizeof(val3) == sizeof(uint32_t)); 36}

追記 (別解)

std::enable_if は関数であれば返却値の型を書く箇所に書くのが普通なのですが、型のテンプレート引数に制約を付けるにあたっては返却値というものがありませんから、ひとつ余分な型引数を用意するのはよく使われるイディオムです。 この余計な型引数は enabler と呼ばれています。

イディオムとして定着しているのでパターンを覚えておくだけでも十分に使えるとはいうものの、本来の使い方とは言い難い面はあり、よりわかりやすい方法としては std::conditional で選択するというのもひとつの案です。 これも C++11 の範囲で可能です。

cpp

1#include <cassert> 2#include <cstdint> 3#include <type_traits> 4 5template<std::size_t r> 6struct FitSizeVariable { 7 using type = typename std::conditional<r <= UINT8_MAX, uint8_t, typename std::conditional<r<=UINT16_MAX, uint16_t, typename std::conditional<r<=UINT32_MAX, uint32_t, void>::type>::type>::type; 8}; 9 10template<std::size_t r> 11using FitSizeVariable_t = typename FitSizeVariable<r>::type;

ただ、 std::conditional は選ばれなかった側の選択肢も評価の対象にはなるという性質があり、今回の場合は問題ありませんがメタプログラミングの道具としては少し使いづらい面はあります。

それとインデントの付け方にもよりますが、ひとつの式に詰め込む形になるので個人的には見通しが悪いように思います。

投稿2020/06/22 07:37

編集2020/06/22 09:44
SaitoAtsushi

総合スコア5684

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bjnes

2020/06/22 08:00

とてもわかり易く指南して頂きありがとうございます。 std::enable_if typename FitSizeVariable<r>::type; が具体的で良くわかりました。 数値範囲をどのようにしたら部分特殊化できるのか非常に悩んでいたので助かりました。
bjnes

2020/06/22 08:03

あと、std::size_tも、そのとおりです。uint64じゃないですね。 ありがとうございます
guest

0

SaitoAtsushi さん回答の焼き直しですが、C++17の constexpr if文 を使って手続き型風に書くこともできます。

c++

1#include <cassert> 2#include <cstdint> 3#include <type_traits> 4 5template <std::size_t Range> 6class FitSizeVariable 7{ 8 static constexpr auto type_selector() 9 { 10 if constexpr (Range <= UINT8_MAX) { 11 return uint8_t{}; 12 } 13 else if constexpr (Range <= UINT16_MAX) { 14 return uint16_t{}; 15 } 16 else if constexpr (Range <= UINT32_MAX) { 17 return uint32_t{}; 18 } 19 else { 20 static_assert((Range * 0), "too wide range"); 21 } 22 } 23public: 24 using type = decltype(type_selector()); 25}; 26 27template <std::size_t Range> 28using FitSizeVariable_t = typename FitSizeVariable<Range>::type; 29 30 31int main() 32{ 33 FitSizeVariable_t<UINT8_MAX> val1{}; 34 FitSizeVariable_t<UINT16_MAX> val2{}; 35 FitSizeVariable_t<UINT32_MAX> val3{}; 36// FitSizeVariable_t<UINT64_MAX> val4{}; 37 38 assert(sizeof(val1) == sizeof(uint8_t)); 39 assert(sizeof(val2) == sizeof(uint16_t)); 40 assert(sizeof(val3) == sizeof(uint32_t)); 41}

投稿2020/06/22 13:43

yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問