3

少し前に、「パラメーター化された」ユーザー定義リテラルについてのアイデアがあり、現在の C++ 標準でこれを行う方法があるかどうか疑問に思っていました。

基本的に、アイデアは、いくつかのパラメーターに従って動作を微調整できるユーザー定義のリテラルを持つことです。簡単な例として、浮動小数点数を整数に変換する「固定小数点」リテラルを選択しました。パラメータは、小数点以下の桁数に関する精度です。

これが実際のアプリケーションでどのように役立つか、または役立つかどうかはわからないため、これは今のところ演習にすぎません。

私の最初のアイデアは次のようなものでした:

namespace fp_impl {
    constexpr int floor(long double n) {
        return n;
    }

    constexpr int pow10(int exp) {
        return exp == 0 ? 1 : 10 * pow10(exp - 1);
    }

    template<int i>
    constexpr int fixed_point(long double n) {
        return floor(n * pow10(i));
    }

    namespace fp2 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<2>(n);
        }
    }

    namespace fp4 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<4>(n);
        }
    }
}

template<int prec> struct fp;
template<> struct fp<2> {
    namespace lit = fp2;
};
template<> struct fp<4> {
    namespace lit = fp4;
};

int main() {
    {
        using namespace fp<2>::lit;
        std::cout << 5.421_fp << std::endl; // should output 542
    }
    {
        using namespace fp<4>::lit;
        std::cout << 5.421_fp << std::endl; // should output 54210
    }
}

ただし、名前空間エイリアスはクラス スコープで許可されていないため、コンパイルされません。(また、 のすべてのバージョンを手動で定義する必要があるという問題もありますoperator"" _fp。) そこで、マクロを使って何かを試すことにしました。

namespace fp {
    namespace detail {
        constexpr int floor(long double n) {
            return n;
        }

        constexpr int pow10(int exp) {
            return exp == 0 ? 1 : 10 * pow10(exp - 1);
        }

        template<int i>
        constexpr int fixed_point(long double n) {
            return floor(n * pow10(i));
        }
    }
}

#define SPEC(i) \
    namespace fp { \
        namespace precision##i { \
            constexpr int operator"" _fp(long double n) { \
                return fp::detail::fixed_point<i>(n); \
            } \
        } \
    }
SPEC(2); SPEC(4);
#undef SPEC
#define fp_precision(i) namespace fp::precision##i

int main() {
    {
        using fp_precision(2);
        std::cout << 5.421_fp << std::endl;
    }
    {
        using fp_precision(4);
        std::cout << 5.421_fp << std::endl;
    }
}

これは機能しますが、使用するSPEC()すべての精度に対してマクロを使用する必要があります。もちろん、プリプロセッサのトリッキーを使用して、たとえば 0 から 100 までのすべての値に対してこれを行うこともできますが、必要に応じてそれぞれがインスタンス化されるテンプレート ソリューションのようなものがあるのではないかと考えています。テンプレートクラスでフレンド関数として宣言された operator"" を使用するという漠然とした考えがありましたが、それもうまくいかないと思います。

注意として、試してみtemplate<int i> constexpr int operator"" _fp(long double n)ましたが、これはリテラル演算子の許可された宣言ではないようです。

4

3 に答える 3

6

operator()(int)リテラル演算子からオーバーロードされたクラス型を返すことができます。それからあなたは書くことができます

5.421_fp(2);
于 2012-07-06T22:04:14.517 に答える
2

ユーザー定義のリテラル関数は、その唯一の引数としてリテラル自体を取ります。たとえば、グローバル変数またはスレッドローカル変数を使用して、関数の外部で状態を使用できますが、それはあまりきれいではありません。

引数が常にコンパイル時の定数であり、それが数値の一部である場合は、それをリテラル自体に渡します。これには、operator "" _ ( char const *, std::size_t )オーバーロードまたはtemplate< char ... > operator "" _ ()テンプレートを作成し、自分で数値を完全に解析する必要があります。

ただし、そのようなパラメーターを既存の浮動小数点文法に組み込む必要があります。C++ は非常に自由度の高い前処理番号構造を定義していますが、ユーザー定義のリテラルは、 ud-suffix識別子が追加された有効なトークンから形成する必要があります。

数字の代わりに文字列を使用することを検討するかもしれませんが、テンプレート オプションはなくなります。

于 2012-07-05T10:17:40.080 に答える
0

問題を解決するためにマクロは必要ありません。この問題はリテラル数 (整数や浮動小数点フォーマットの数など) の処理に関するものであるため、リテラル演算子のテンプレート定義とテンプレート メタプログラミングを使用して、コンパイル時に完全にジョブを実行できます。

固定小数点リテラル変換を​​行うには、整数リテラル演算子を unsigned long long と共に使用できます。

some_type operator "" _fp(unsigned long long num)
{
  // code
}

(または精度が失われる可能性のある long double を使用)が、これによりすべてが実行時に発生します。

C++11 のセクション 2.14.8 (User-defined Lierals [lex.ext]) の段落 3 と 4では、整数および浮動小数点リテラルのテンプレート バージョン含む、リテラル演算子のバリエーションが定義されています。残念ながら、パラグラフ 5 と 6は、文字列文字リテラルのテンプレート バージョン定義していません。これは、この手法が整数リテラルと浮動小数点リテラルでのみ機能することを意味します。

したがって、C++11 セクション 2.14.8 から、上記の _fp リテラル演算子は、代わりに次のように記述できます。

template <char... Digits>
constexpr some_type operator "" _fp()
{
  return process_fp<2, Digits...>::to_some_type();
}

たとえば、2 はint iOP のテンプレート パラメータの値であり、some_type必要な戻り値の型です。テンプレート パラメータが -- やその他の数値ではないことに注意してcharくださいint。また、リテラル演算子には引数がないことに注意してください。Digit - '0'したがって、数値をその文字の整数値にするには、次のようなコードが必要です。また、Digits...左から右の順に処理されます。

process_fpこれで、前方宣言が次のようになるテンプレート メタプログラミングを使用できます。

template <int i, char... Digits>
struct process_fp;

また、必要なコンパイル時の結果を計算して返すためにstatic constexpr呼び出されるメソッドがあります。to_some_type()

これが行われた意味のある簡単な例が必要になる場合もあります。昨年、私は次のように使用されるコード (以下のリンク) を書きました。

int main()
{
  using namespace std;

  const unsigned long long bits =
    11011110101011011011111011101111_binary;
  cout << "The number is: " << hex << bits << endl;
}

コンパイル時に 2 進数 1101111010101101101111011101111 を に変換し、unsigned long longビットに格納します。上記のテンプレート メタプログラミング手法を使用した完全なコードと説明は、私のブログ エントリUsing The C++ Literal Operatorで提供されています。

于 2012-07-09T07:18:29.933 に答える