constexpr 参数化函数指针

constexpr parametrized function pointer

我有以下第三方API:

using StatisticsFunc = double (*)(const std::vector<double> &)
libraryClass::ComputeStatistics(StatisticsFunc sf);

我是这样使用的:

obj1->ComputeStatistics([](const auto& v) {return histogram("obj1", v);};
obj2->ComputeStatistics([](const auto& v) {return histogram("obj2", v);};

但是所有这些 lambda 都只是重复的代码。我宁愿这样:

obj1->ComputeStatistics(getHistogramLambda("obj1"));

所以我需要定义:

constexpr auto getHistogramLambda(const char* name) {
    return [](const auto& v) {return histogram(name, v);};
}

但是不行,因为name没有被捕获。这也行不通:

constexpr auto getHistogramLambda(const char* name) {
    return [name](const auto& v) {return histogram(name, v);};
}

因为捕获 lambda 不再是无状态的,不能转换为函数指针。

Ofc 可以将它作为一个宏来实现,但我想要一个现代的 C++ 17 解决方案。

将字符串作为模板参数传递似乎也是一种选择: ,但我很好奇是否有 constexpr 的方法。

有点。

这个:

obj1->ComputeStatistics(getHistogramLambda("obj1"));

由于您指出的原因而无法工作 - 您需要捕获状态。然后,我们不能这样写:

obj1->ComputeStatistics(getHistogramLambda<"obj1">());

因为虽然我们可以使用 const char* 类型的模板参数,但我们不能将它们绑定到字符串文字。你可以这样做:

template <const char* name>
constexpr auto getHistogramLambda() {
    return [](const auto& v) {return histogram(name, v);};
}

const char p[] = "obj1";
obj1->ComputeStatistics(getHistogramLambda<p>());

这很尴尬,因为您需要为每次调用引入额外的变量。在 C++20 中,我们将能够编写一个 class 类型,其模板参数是一个固定字符串,这将允许 getHistogramLambda<"obj1"> 工作,只是方式略有不同。

到那时,目前最好的方法可能是使用 UDL 捕获单个字符作为某些 class 类型的模板参数:

template <char... Cs>
constexpr auto getHistogramLambda(X<Cs...>) {
    static constexpr char name[] = {Cs..., '[=13=]'};
    return [](const auto& v) { return histogram(name, v);};
}


obj->ComputeStatistic(getHistogramLambda("obj1"_udl));

这里的意图是 "obj"_udl 是类型 X<'o', 'b', 'j', '1'> 的对象 - 然后我们以仍然不需要捕获的方式在函数模板的主体内重建字符串。

避免重复是否值得?可能是。

不确定您到底需要什么,但是...声明一个全局 constexpr char const 指针数组怎么样

constexpr std::array<char const *, 3u> arrStr {{"obj0", "obj1", "obj2"}};

然后在 getHistogramLambda() 中接收所需字符串的索引作为模板参数?

template <std::size_t N>
constexpr auto getHistogramLambda () {
    return [](const auto& v) {return histogram(arrStr.at(N), v);};
}

这样就可以调用ComputeStatistic()如下

obj1->ComputeStatistics(getHistogramLambda<1u>());

不同的答案,由 Michael Park 提供。我们可以在类型中编码我们想要的值——不是将我们想要的字符串文字作为函数参数或模板参数传递,而是作为实际类型传递——这样我们就不需要捕获它:

#define CONSTANT(...) \
  union { static constexpr auto value() { return __VA_ARGS__; } }
#define CONSTANT_VALUE(...) \
  [] { using R = CONSTANT(__VA_ARGS__); return R{}; }()


template <typename X>
constexpr auto getHistogramLambda(X) {
    return [](const auto& v) { return histogram(X::value(), v);};
}

obj->ComputeStatistic(getHistogramLambda(CONSTANT_VALUE("obj1")));
obj->ComputeStatistic(getHistogramLambda(CONSTANT_VALUE("obj2")));

不确定在这种特殊情况下这是否比 UDL 方法更好,但这肯定是一种有趣的技术。