【问题标题】:Using constexpr function as template parameter使用 constexpr 函数作为模板参数
【发布时间】:2023-03-16 02:25:01
【问题描述】:

我正在尝试将 constexpr 函数的结果用作模板参数,但无法弄清楚如何使其工作。我有以下代码:

#include <functional>
#include <string_view>

class slice
{
    public:
        template <std::size_t size>
        constexpr slice(char const (&data)[size]) noexcept :
            _size(size),
            _data(data)
        {}

        constexpr const char *data() const
        {
            return _data;
        }

        constexpr std::size_t size() const
        {
            return _size;
        }
    private:
        const size_t    _size;
        const char      *_data;
};

template <std::size_t size>
class key
{
    public:
        constexpr key(std::size_t hash, const char *data) :
            _hash(hash),
            _data(data, data + size)
        {}
    private:
        std::size_t             _hash;
        std::array<char, size>  _data;
};

class partition
{
    public:
        partition(std::string_view name) :
            _hash(std::hash<std::string_view>{}(name))
        {}

        auto operator()(const slice &data)
        {
            return key<data.size()>(_hash, data.data());
        }
    private:
        const std::size_t   _hash;
};

甚至不尝试使用这些类,它就拒绝编译。我收到以下错误:

错误:非类型模板参数不是常量表达式

       return key<data.size()>(_hash, data.data());

我试图不将 operator() 的参数作为参考,但这只会增加更多警告消息。我不能将 _data 成员设为 constexpr,因为显然这是不允许的。

我会使用如下代码:

partition partition1{ "partition 1" };
partition partition2{ "partition 2" };

auto key1 = partition1("key 1");
auto key2 = partition2("key 1");

这意味着存储系统能够轻松创建不同的分区(或存储桶)。

【问题讨论】:

  • 如果你不能拥有partitionconstexpr的构造函数,那么拥有operator()constexpr的目的是什么?
  • @Holt:我不太明白你的意思。这个想法是 operator() 可以使用正确的模板参数自动创建一个键实例。分区构造函数不是 constexpr 完全无关紧要。
  • 为了在partition 的实例上调用operator(),您需要首先创建partition 的实例。如果您没有constexpr 构造函数,您将永远无法创建constexpr partition,因此您将永远无法在constexpr 上调用operator(),因此constexpr 限定符是无用的。
  • 好的,我将从 operator() 中删除 constexpr 限定符。不过,这并没有改变原来的问题:如何让 operator() 选择正确的模板参数来构造键。
  • 看我的回答。如果您想了解更多详细信息,则需要在实际调用此运算符的位置提供示例代码。

标签: c++ c++17


【解决方案1】:

您的代码无法编译,因为:

constexpr auto operator()(const slice &data)
{
    return key<data.size()>(_hash, data.data());
}

...data 不保证是constexpr。您可以使用模板参数:

template <const slice& data>
constexpr auto operator()()
{
    return key<data.size()>(_hash, data.data());
}

...但是,如果由于非constexpr 构造函数而无法实例化constexpr partition,那么创建constexpr 的意义何在?

【讨论】:

  • 这当然行不通。模板参数不能是类实例。
  • @MartijnOtto 如果dataconstexpr 则有效,但当然它不适用于非constexpr 数据。如果data 不是-constexpr,则无法在编译时访问data.size(),因此无法使用data.size() 作为模板参数。
  • @ildjarn 不,不是。您可以使用左值引用作为模板参数。参见例如herehere。当然,你不能像 &lt;"key 1"&gt; 这样称呼它,并期望编译器从 const char[] 创建一个 const slice&amp;,但那是另一回事了。
  • 好吧,我的编译器(clang 3.8.0)不接受它:错误:二进制表达式('partition'和'slice')的无效操作数,是的,当然是 const 数据。
  • @MartijnOtto 工作得很好here(错误与此无关)。 const 不是constexpr,你需要一个静态的slice 才能工作。这当然不适用于您的情况,但在您发布实际使用情况之前我无法猜测。