【发布时间】:2021-12-07 15:13:15
【问题描述】:
以下 C++-20 代码允许滥用 lambda 来存储编译时值:
#include <type_traits>
#include <iostream>
consteval auto addItem(auto origin, auto key, auto value)
requires (std::is_same_v<void, decltype(origin(key))>) {
auto result = [=](auto arg) constexpr
{
if constexpr(std::is_same_v<decltype(arg), decltype(key)>) {
return value;
}
else if constexpr(!std::is_same_v<void, decltype(origin(arg))>)
{
return origin(arg);
}
};
return result;
}
template<int I> struct index {};
int main()
{
constexpr auto c = [](auto) {};
constexpr auto c_ = addItem(c, index<1>{}, "Hello");
constexpr auto c__ = addItem(c_, index<2>{}, 42);
// Will fail to compile as expected, since requires clause above kicks in
// constexpr auto c___ = addItem(c__, index<2>{}, "WAT");
std::cout << c__(index<1>{}) << " ";
std::cout << c__(index<2>{}) << "\n";
// Will fail to compile as expected, since return type is void
// This is the point where I wish I had a more expressive error message
// std::cout << c__(index<3>{}) << "\n";
}
这很好,但是公共接口有点笨拙,所以我想直接使用索引值来解决这个问题:
#include <type_traits>
#include <iostream>
template<int I>
consteval auto addItem(auto origin, auto value)
requires (std::is_same_v<void, decltype(origin(I))>) {
auto result = [=]<int J>() constexpr
{
if constexpr(I == J) {
return value;
}
else
{
// Fails to compile
return origin<J>();
}
};
return result;
}
int main()
{
constexpr auto c = []<int I>() {};
constexpr auto c_ = addItem<1>(c, "Hello");
constexpr auto c__ = addItem<2>(c_, 42);
std::cout << c__<1>() << " ";
std::cout << c__<2>() << "\n";
}
此尝试失败并显示错误消息
Output of x86-64 clang (trunk) (Compiler #1)
<source>:15:20: error: 'origin' does not name a template but is followed by template arguments
return origin<J>();
^ ~~~
看起来 auto 不像我希望的那样自动。在不更改公共界面的情况下,您知道如何实现我的目标吗?
【问题讨论】:
-
这是不可能的,因为当您编写
constexpr auto c = ...时,您永远不会获得可以直接向其提供模板参数的实体,如c<I>。你总是得到一个具体的对象(当然,它可能有模板化的成员)。 -
由于
c.template operator()<2>();是有效代码,我倾向于不同意..