在阅读关于constexpr的幻灯片时,介绍是关于"令人惊讶的动态初始化与consts".这个例子是
struct S {
static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;
Run Code Online (Sandbox Code Playgroud)
唉,音轨缺失了,音符也是如此,所以我只能猜到这里的含义.
是否正确d地"动态地"初始化,因为之前S::c定义了吗? d该声明的S::c是之前d可能是不够的,编译器需要完整的定义,对不对?
那就是说,我怀疑,在下面的例子中d 会静态初始化?
struct S {
static const int c;
};
const int S::c = 5;
const int d = 10 * S::c; // now _after_ defn of S::c
Run Code Online (Sandbox Code Playgroud)
并采取蛋糕,在C++ 11,什么必须是constexpr完全静态初始化?S::c …
在编译时搜索了一种检查endianess的方法后,我提出了以下解决方案:
static const int a{1};
constexpr bool is_big_endian()
{
return *((char*)&(a)) == 1;
}
Run Code Online (Sandbox Code Playgroud)
GCC仅在需要constexpr的某些上下文中接受此代码:
int b[is_big_endian() ? 12 : 25]; //works
std::array<int, testendian() ? 12 : 25> c; //fails
Run Code Online (Sandbox Code Playgroud)
对于第二种情况,GCC说error: accessing value of ‘a’ through a ‘char’ glvalue in a constant expression.我无法在禁止此类事件的标准中找到任何内容.也许有人可以澄清GCC在哪种情况下是正确的?
我之前根据参数是否constexpr询问函数重载.我正试图解决这个问题的令人失望的答案,以建立一个更聪明的断言功能.这大致是我想做的事情:
inline void smart_assert (bool condition) {
if (is_constexpr (condition))
static_assert (condition, "Error!!!");
else
assert (condition);
}
Run Code Online (Sandbox Code Playgroud)
基本上,我们的想法是编译时检查总是比运行时检查更好,如果可以在编译时检查.但是,由于内联和常量折叠之类的东西,我不能总是知道是否可以进行编译时间检查.这意味着可能存在assert (condition)编译的情况,assert(false)代码只是等待我运行它并在我发现错误之前执行该路径.
因此,如果有某种方法来检查条件是否是constexpr(由于内联或其他优化),我可以static_assert在可能的情况下调用,否则返回运行时断言.幸运的是,gcc具有内在函数__builtin_constant_p (exp),如果exp是constexpr 则返回true .我不知道其他编译器是否有这种内在的,但我希望这可以解决我的问题.这是我提出的代码:
#include <cassert>
#undef IS_CONSTEXPR
#if defined __GNUC__
#define IS_CONSTEXPR(exp) __builtin_constant_p (exp)
#else
#define IS_CONSTEXPR(exp) false
#endif
// TODO: Add other compilers
inline void smart_assert (bool const condition) {
static_assert (!IS_CONSTEXPR(condition) or condition, "Error!!!");
if (!IS_CONSTEXPR(condition))
assert (condition);
}
#undef IS_CONSTEXPR
Run Code Online (Sandbox Code Playgroud)
该static_assert …
假设我有一个constexpr函数指针数组,我想编写一个constexpr函数来查找指定函数的数组索引.
我可能有这样的代码:
void test1(){}void test2(){}void test3(){}void test4(){}
typedef void(*func)(void);
constexpr func funcs[] = { &test1, &test2, &test3 };
constexpr int FindMatchingIdx (const func work, const int idx) {
return (work == funcs[idx]) ? (idx) : (FindMatchingIdx(work, idx + 1));
}
constexpr unsigned int loc = FindMatchingIdx (&test1,0);
Run Code Online (Sandbox Code Playgroud)
现在这个代码在Clang和MSVC上编译,但是GCC只会FindMatchingIdx在使用数组中的第一个元素调用时进行编译.如果FindMatchingIdx调用test1,GCC将编译代码,但如果FindMatchingIdx调用,test2或test3GCC将无法编译代码,给出错误消息:
错误:'(test1!= test2)'不是常量表达式.
如果FindMatchingIdx必须递归,GCC将无法将其视为constexpr功能.这是GCC中的错误吗?函数指针比较如何在constexpr函数内部工作?显然,它不能使用由链接器分配的实际指针值.
VS2017 15.1无法编译以下代码:
template<int data_size>
struct Data { };
template<int s>
struct Base
{
static constexpr int size() { return s; }
};
template<int s>
struct Derived : Base<s> // struct Derived
{
Data<Base<s>::size()> data;
};
int main()
{
Derived<1> c;
}
Run Code Online (Sandbox Code Playgroud)
错误是:
Run Code Online (Sandbox Code Playgroud)error C2975: 'data_size': invalid template argument for 'Data', expected compile-time constant expression note: see declaration of 'data_size' note: see reference to class template instantiation 'Derived<s>' being compiled
如果我不是从中衍生Derived出来的Base,那么错误就会消失.使用gcc 5.4.0和clang 4.0.0在这两种情况下一切都很好.
这段代码有什么问题吗?
std::exchange,在C++ 14中引入,规定如下:
Run Code Online (Sandbox Code Playgroud)template< class T, class U = T > T exchange( T& obj, U&& new_value );替换
objwith 的值new_value并返回旧值obj.
以下是cppreference的可能实现:
template<class T, class U = T>
T exchange(T& obj, U&& new_value)
{
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
Run Code Online (Sandbox Code Playgroud)
据我所知,没有什么可以防止std::exchange被标记为constexpr.有没有理由我错过了为什么不能constexpr,或者这只是一个疏忽?
假设我有一个magic number想要摆脱的...
//whatever.cpp
for (int i = 0; i < 42; i++)
{
//...
}
Run Code Online (Sandbox Code Playgroud)
我可以通过两种方式杀死它:
在源文件中带有const int SOMETHING_SOMETHING_MEANING_OF_LIFE = 42
或带有。constexpr int SOMETHING_SOMETHING_MEANING_OF_LIFE = 42.cpp
在这种情况下,两者之间是否有任何有意义的区别(我记得编译器推断出-在任何一种情况下-值均未更改,因此42实际上将其硬编码在结果循环/展开循环/任何机器代码中)或归结为个人品味吗?
在一个相关的问题中:如果magic number在标头(.h)文件而不是源(.ccp)文件中声明了(从而替换它的事物),那会发生什么变化(如果是,则如何改变)呢?
下面的代码合法吗?
template <int N>
class foo {
public:
constexpr foo()
{
for (int i = 0; i < N; ++i) {
v_[i] = i;
}
}
private:
int v_[N];
};
constexpr foo<5> bar;
Run Code Online (Sandbox Code Playgroud)
Clang 接受它,但 GCC 和 MSVC 拒绝它。
GCC 的错误是:
main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
15 | constexpr foo<5> bar;
| ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
4 …Run Code Online (Sandbox Code Playgroud) c++ compile-time-constant template-meta-programming constexpr
我一直在尝试在编译时计算的 const 表达式。但是我玩了一个在编译时执行时看起来非常快的示例。
#include<iostream>
constexpr long int fib(int n) {
return (n <= 1)? n : fib(n-1) + fib(n-2);
}
int main () {
long int res = fib(45);
std::cout << res;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我运行此代码时,大约需要 7 秒才能运行。到现在为止还挺好。但是,当我更改long int res = fib(45)到const long int res = fib(45)它需要没有哪怕一秒钟。据我了解,它是在编译时评估的。
但是编译大约需要0.3秒
编译器怎么能这么快地评估它,但在运行时却需要更多的时间?我正在使用 gcc 5.4.0。
从 C++20 开始,我们可以在编译时分配内存,我们必须在编译时释放它。因此,这给我提出了一些问题:首先,为什么这行得通?
constexpr int* return_ptr() {
return new int{ 1 };
}
void call_return_ptr(){
int* int_ptr = return_ptr();
}
int main() {
call_return_ptr();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我正在使用从编译时返回的 ptrconstexpr并且没有释放它。根据我的理解,这应该是一个错误。其次,如果第一个示例有效,那么为什么这不起作用:
constexpr int* return_ptr() {
return new int{ 1 };
}
void call_return_ptr(){
constexpr int* int_ptr = return_ptr();
}
int main() {
call_return_ptr();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
即使我们在编译时尝试删除指针,如下所示:
constexpr int* return_ptr() {
return new int{ 1 };
}
constexpr void call_return_ptr(){
constexpr int* int_ptr = return_ptr();
delete int_ptr;
} …Run Code Online (Sandbox Code Playgroud)