由于在运行时可能会调用声明为constexpr的函数,编译器在哪个条件下决定是在编译时还是在运行时计算它?
template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
return (expo != 0 )? base * POW(base, expo -1) : 1;
}
int main(int argc, char** argv)
{
int i = 0;
std::cin >> i;
std::cout << POW(i, 2) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我在编译时是未知的,这可能是编译器将POW()视为在运行时调用的常规函数的原因.然而,这种动态虽然看起来很方便,但却具有一些不切实际的含义.例如,是否有一种情况我希望编译器在编译时计算constexpr函数,编译器决定将其视为普通函数,而它在编译时也会工作?有任何已知的常见陷阱吗?
如何static_assert在一个constexpr函数中正确执行?例如:
constexpr int do_something(int x)
{
static_assert(x > 0, "x must be > 0");
return x + 5;
}
Run Code Online (Sandbox Code Playgroud)
这不是有效的C++ 11代码,因为constexpr函数必须只包含return语句.我不认为该标准有例外,但GCC 4.7不允许我编译这段代码.
仅仅因为一个函数(或构造函数)......
...并不意味着编译器将在转换期间评估constexpr函数.我一直在查看C++ 11 FDIS(N3242,可从http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/获取)来尝试确定两件事:
第5.19节第1段说明在翻译过程中可以评估常量表达式.据我所知,第5.19节的其余部分阐述了constexpr函数定义中有效的规则.
我理解我可以通过将constexpr函数的结果声明为constexpr来强制转换期间的constexpr评估.像这样:
// Declaration
constexpr double eulers_num() { return 2.718281828459045235360287471; }
// Forced evaluation during translation
constexpr double twoEulers = eulers_num() * 2.0;
static_assert(twoEulers > 5.0, "Yipes!");
Run Code Online (Sandbox Code Playgroud)
到目前为止,我一直无法找到FDIS中的段落:
twoEulers在翻译过程中要评估的力量我对发现特别感兴趣的是翻译期间的constexpr评估是否由以下因素触发:
如果可能的话,请在您的回复中引用我可以查找的FDIS部分或我可以在FDIS中搜索的关键短语.标准中的英语有点迟钝,所以我可能已经阅读了相关的段落并完全忽略了它们的含义或意图.
我真的找不到任何用途.我的第一个想法是,我可以使用它来实现'按合同设计',而不使用像这样的宏:
struct S
{
S(constexpr int i) : S(i) { static_assert( i < 9, "i must be < 9" ); }
S(int i); //external defintion
char *pSomeMemory;
};
Run Code Online (Sandbox Code Playgroud)
但这不会编译.我想我们也可以使用它来引用同一变量,而不需要创建额外的内存,当我们想要避免get/setters时,为了使用户的一个成员的实例是只读的:
class S
{
private:
int _i;
public:
const int & constexpr i = _i;
};
Run Code Online (Sandbox Code Playgroud)
但以上都没有实际编译过.有人可以告诉我为什么要引入这个关键字?
几天前,我询问编译器决定是否在编译期间计算constexpr函数.
事实证明,只有在编译时才会评估constexpr,如果所有参数都是常量表达式,并且您指定给它的变量也是常量表达式.
template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
return (expo != 0 )? base * POW(base, expo -1) : 1;
}
template<typename T>
void foobar(T val)
{
std::cout << val << std::endl;
}
int main(int argc, char** argv)
{
foobar(POW((unsigned long long)2, 63));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我被告知是真的,这个代码示例是非常不实际的,因为foobar不接受constexpr(由于某种原因你不能使用consexpr作为参数),POW在运行时被评估,即使它可能是可能的在编译期间计算它.强制编译时评估的明显解决方案是:
auto expr = POW((unsigned long long)2, 63);
foobar(expr);
Run Code Online (Sandbox Code Playgroud)
然而,这迫使我使用额外的代码行,每次我想确保在编译时评估constexpr时都不需要这样做.为了使这更方便一点,我想出了以下可疑的宏:
#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()
foobar(FORCE_CT_EVAL(POW((unsigned long long)2, 63)));
Run Code Online (Sandbox Code Playgroud)
尽管它工作得很好,但我觉得好像有些不对劲.创建匿名lambda会影响性能吗?通过rvalue引用返回实际上是将表达式移动到函数参数吗?std :: move如何影响性能?有没有更好的单线解决方案呢?
我正在寻找一种方法将通用(constexpr,显然)功能传递给模板.它必须能够在不使用lambda的情况下获取任何数量的参数.这是我到目前为止:
template<typename T, T(*FUNC)()> struct CALL
{
static inline constexpr decltype(FUNC()) EXEC()
{
return FUNC();
}
};
Run Code Online (Sandbox Code Playgroud)
但是,只有传递的函数不带参数时才有效.有没有办法让模板接受任何constexpr功能?传递std ::函数似乎不起作用.我想关键是可变参数模板参数,但我不知道在这种情况下如何利用它们.