web-dev-qa-db-ja.com

「constexpr」を使用してテンプレートパラメータに文字列リテラルを使用する

intを使用してconst char*constexprにキャストするコードをいくつか作成したので、const char*をテンプレート引数として使用できます。コードは次のとおりです。

#include <iostream>

class conststr
{
    public:
        template<std::size_t N>
        constexpr conststr(const char(&STR)[N])
        :string(STR), size(N-1)
        {}

        constexpr conststr(const char* STR, std::size_t N)
        :string(STR), size(N)
        {}

        constexpr char operator[](std::size_t n)
        {
            return n < size ? string[n] : 0;
        }

        constexpr std::size_t get_size()
        {
            return size;
        }

        constexpr const char* get_string()
        {
            return string;
        }

        //This method is related with Fowler–Noll–Vo hash function
        constexpr unsigned hash(int n=0, unsigned h=2166136261)
        {
            return n == size ? h : hash(n+1,(h * 16777619) ^ (string[n]));
        }

    private:
        const char* string;
        std::size_t size;
};

// output function that requires a compile-time constant, for testing
template<int N> struct OUT
{
    OUT() { std::cout << N << '\n'; }
};

int constexpr operator "" _const(const char* str, size_t sz)
{
    return conststr(str,sz).hash();
}

int main()
{
    OUT<"A dummy string"_const> out;
    OUT<"A very long template parameter as a const char*"_const> out2;
}

このサンプルコードでは、outのタイプはOUT<1494474505>であり、out2のタイプはOUT<106227495>です。このコードの背後にある魔法はconststr::hash()です FNVハッシュ関数 を使用するconstexpr再帰です。このようにして、const char *の整数ハッシュを作成します。これは、おそらくユニークなハッシュです。

この方法についていくつか質問があります。

  1. これは安全に使用できるアプローチですか?それとも、このアプローチは特定の用途で悪になる可能性がありますか?
  2. 文字数に制限されることなく、文字列ごとに異なる整数を作成するより良いハッシュ関数を書くことができますか? (私の方法では、長さは十分に長いです)
  3. const char*conststrを介してint constexprに暗黙的にキャストするコードを記述できます。したがって、美的に醜い(そして時間を消費する)_constユーザー定義の文字列リテラルは必要ありません。たとえば、OUT<"String">は有効です( "String"を整数にキャストします)。

どんな助けでもありがたいです、どうもありがとう。

このメソッドは非常に興味深いものですが、文字列リテラルをテンプレート引数として渡す方法ではありません。実際、これは文字列リテラルに基づくテンプレート引数のジェネレーターです。これは同じではありません。stringhashed_stringから取得することはできません...これは、テンプレート内の文字列リテラルの興味を完全に覆します。 。

[〜#〜] edit [〜#〜]:使用されたハッシュが文字の加重和であり、 OPの編集後のケース。

Mitchnullの回答で述べられているように、 ハッシュ関数 にも問題がある可能性があります。これは、メソッドのもう1つの大きな問題、衝突です。例えば:

// Both outputs 3721
OUT<"0 silent"_const> out;
OUT<"7 listen"_const> out2;

私の知る限り、現在の標準では、テンプレート引数で文字列リテラルを直接渡すことはできません。ただし、「偽造」することはできます。これが私が一般的に使用するものです:

struct string_holder              //
{                                 // All of this can be heavily optimized by
    static const char* asString() // the compiler. It is also easy to generate
    {                             // with a macro.
        return "Hello world!";    //
    }                             //
};                                //

次に、型引数を介して「偽の文字列リテラル」を渡します。

template<typename str>
struct out
{
    out()
    {
        std::cout << str::asString() << "\n";
    }
};

EDIT2:コメントで、クラステンプレートのいくつかの特殊化を区別するためにこれを使用したと述べました。あなたが示した方法はそのために有効ですが、タグを使用することもできます:

// tags
struct myTag {};
struct Long {};
struct Float {};

// class template
template<typename tag>
struct Integer
{
    // ...
};
template<> struct Integer<Long> { /* ... */ };

// use
Integer<Long> ...;  // those are 2
Integer<Float> ...; // different types
13
Synxis

これが、テンプレートのconst文字列パラメーターに使用しているパターンです。 class F { static constexpr const char conststr[]= "some const string"; TemplateObject<conststr> instance; };

参照: https://stackoverflow.com/a/18031951/782168

6
h4ck3rm1k3