【问题标题】:Using "constexpr" to use string literal for template parameter使用“constexpr”将字符串文字用于模板参数
【发布时间】:2013-04-07 11:05:24
【问题描述】:

我已经编写了一些代码,通过使用constexpr 将const char* 转换为int,因此我可以使用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&lt;1494474505&gt;,out2 的类型为OUT&lt;106227495&gt;。这段代码背后的魔法是conststr::hash(),它是一个使用FNV Hash function 的constexpr 递归。因此它为 const char* 创建了一个完整的散列,希望它是唯一的散列。

我对此方法有一些疑问:

  1. 这是一种安全的使用方法吗?或者这种方法在特定用途中可能是一种邪恶?
  2. 您能否编写一个更好的散列函数,为每个字符串创建不同的整数,而不受字符数的限制? (按我的方法,长度够长了)
  3. 您能否编写一个代码,通过conststr 将const char* 隐式转换为int constexpr,这样我们就不需要美观的丑陋(以及时间消耗)_const 用户定义的字符串文字?例如OUT&lt;"String"&gt; 将是合法的(并将“字符串”转换为整数)。

任何帮助将不胜感激,非常感谢。

【问题讨论】:

  • 如果我的回答“解决”了您的问题,是否可以将其标记为已接受?否则,您可以(再次)评论它!
  • 哦,对不起,我总是忘记这样做:)

标签: c++ templates c++11 string-literals constexpr


【解决方案1】:

虽然你的方法很有趣,但它并不是真正将字符串文字作为模板参数传递的方法。事实上,它是一个基于字符串字面量的模板参数生成器,这是不一样的:你不能从hashed_string 中检索string...这有点破坏了字符串字面量在模板中的全部兴趣。

EDIT :当使用的哈希是字母的加权和时,以下是正确的,而在 OP 编辑​​后则不是这样。

您的hash function 也可能有问题,如 mitchnull 的回答所述。这可能是您的方法的另一个大问题,即碰撞。例如:

// 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:您在 cmets 中说过您使用它来区分类模板的几个特化。您展示的方法对此有效,但您也可以使用标签:

// 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

【讨论】:

  • Synxis,我编辑了我的代码。我的散列函数是不专业的,现在它使用 Fowler–Noll–Vo 散列函数。我相信这个功能没有同样的问题。我无法理解你的第一段关于'hashed_string'中的'string'?
  • 你没有将字符串传递给OUT,而是hash()的结果。我不认为您可以将此值转换为字符串...因此,您不能让OUT 输出您在括号中写入的字符串。
  • 哦,我明白了。实际上这对我来说不是问题(当然你的观点是有效的)因为我想制作这样工作的模板:假设我们有一个名为 Integer 的模板,Integer 将不同于 Integer 我会专门化我的模板来表现。
  • 你可以使用标签。我会把它放在我的答案中。
  • 好点。我更喜欢标签,因为它比文字版本更“美观”,但这个选择取决于你。您还可以选择不同的文字名称,例如_tag。
【解决方案2】:

这是我用于模板 const 字符串参数的模式。 class F { static constexpr const char conststr[]= "some const string"; TemplateObject<conststr> instance; };

见: https://stackoverflow.com/a/18031951/782168

【讨论】:

    猜你喜欢
    • 2017-03-03
    • 1970-01-01
    • 2017-01-05
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 2019-03-15
    • 1970-01-01
    相关资源
    最近更新 更多