我知道constexpr
函数返回值在函数返回之前不能是编译时间常数。因此,这是有效的:
template <typename...Ts>
constexpr auto f(Ts&&...args) {
auto value = std::tuple<Ts...>(args...);
return some_constexpr_transform_function(value);
}
constexpr auto vf = f(1, 2.3, 4);
然而,对于函数的参数仅为编译时间常数,因此函数能够返回编译时间常数的情况,应该能够使用这些参数并生成编译时间常数来对其进行一些编译时间魔术。
template <typename...Ts>
constexpr auto g(Ts&&...args) {
constexpr auto value = std::tuple<Ts...>(args...);
// do some compile time magic here on value, like:
static_assert(std::get<0>(value) == 1, "The first parameter must be 1.");
return some_constexpr_transform_function(value);
}
constexpr auto vg = g(1, 2.3, 4);
这是因为理解了这个函数不再可以用运行时参数调用。唉,事实并非如此。相反,我不得不做一些丑陋的间接操作,要求事情看起来很混乱,并将初始化放在不合适的位置:
namespace detail {
// Sorry future dev, I know that this init is better off below, but
// I have to make this look ugly to do compile time validation tests.
constexpr auto ugly_intermediate_constexpr_value = std::make_tuple(1, 2.3, 4);
static_assert(std::get<0>(ugly_intermediate_constexpr_value) == 1
, "The first parameter must be 1.");
}
template <typename...Ts>
constexpr auto h(std::tuple<Ts...>&& args) {
return some_constexpr_transform_function(args);
}
constexpr auto vh = h(detail::ugly_intermediate_constexpr_value);
有人说要解决这个问题吗?这似乎是一个相当大的缺陷。
注意:我目前正在使用c++14,我知道我没有使用完美的转发习惯用法。这与问题无关。
制作参数constexpr
的最大问题,甚至P1045也很难处理,与用户在进行常量表达式编码时最希望能够做的事情有关。考虑以下代码:
template<int v>
constexpr auto func()
{
if constexpr(v == 0)
return int{5};
else
return float{20.f};
}
那么,什么是decltype(func)
?答案是没有答案,因为这个问题无效。func
不是函数;它是一个模板。模板没有";类型";,因此CCD_ 5在应用于它们时没有意义。
模板是基于其模板参数生成新类型/函数/变量的构造。以上内容在C++中有效,因为func<0>
是一个与func<1>
不同的函数。因此,decltype(func<0>)
是合法的,并且可以是与decltype(func<1>)
不同的类型。
考虑一个假设的constexpr
等价物:
constexpr auto func2(constexpr int v)
{
if constexpr(v == 0)
return int{5};
else
return float{20.f};
}
好:什么是decltype(func2)
?不可能有答案,因为func2
的作用取决于它被调用的参数。实际上,func2
不是一个函数:它实际上是一种构造,根据给定的参数生成函数。而C++已经有了这样的语言构造。
它被称为";模板";。
这个问题不仅仅涉及函数返回类型(尽管这对于元编程和反射来说是一个很大的问题(。考虑一些简单的事情:
constexpr void func3(constexpr size_t sz)
{
std::array<int, sz> arr{};
//Other stuff.
}
编译器需要生成代码来对arr
进行值初始化。但它的大小必须取决于sz
。实际上,占用的堆栈空间的大小取决于sz
。那么…这是怎么回事?
每个具有不同sz
值的函数调用都必须有效地重新生成函数的内部。。。与模板用于不同模板参数值的方式完全相同。
基本上,任何制作constexpr
参数的尝试都将面临这样一个现实,即这些只是模板参数。它必须建立一个与模板实例化完全相似的机制。
由于C++20已经将类类型作为非类型模板参数,所以唯一需要的就是语法上的糖,允许您调用func(0)
而不是func<0>()
。
所以只需要使用模板参数。