C++ 在构造函数上指定constexpr是否会自动使从它创建的所有对象都成为constexpr?
这是我的密码:C++ 在构造函数上指定constexpr是否会自动使从它创建的所有对象都成为constexpr?,c++,c++11,language-lawyer,constexpr,C++,C++11,Language Lawyer,Constexpr,这是我的密码: class test{ public: constexpr test(){ } constexpr int operator+(const test& rhs){ return 1; } }; int main(){ test t; //constexpr word isn't necessary constexpr int b = t+te
class test{
public:
constexpr test(){
}
constexpr int operator+(const test& rhs){
return 1;
}
};
int main(){
test t; //constexpr word isn't necessary
constexpr int b = t+test(); // works at compile time!
int w = 10; // ERROR constexpr required
constexpr int c = w + 2; // Requires w to be constexpr
return 0;
}
我注意到,即使我没有将test指定为constexpr
,它仍然有效。我尝试通过对int
执行相同的操作来复制结果,但出现了错误。具体来说,它希望我的int w
位于constexpr int c=w+2代码>将成为constexpr
。从我第一次尝试使用test
开始,是否因为我已经在构造函数上使用了constexpr
?如果是这种情况,那么假设所有在其构造函数上具有constexpr
的类都将导致所有用它实例化或创建的对象都是constexpr
好吗
奖金问题:
如果我有一个constexpr
构造函数,这样做不好吗<代码>测试*t=新测试()代码> 根据我的实验,constexpr关键字或多或少地指示编译器必须能够静态解析该调用中给出的所有代码路径。也就是说,至少现在(看起来),所有内容都必须沿着该代码路径声明为constexpr,否则它将失败。例如,在代码中,如果不声明运算符或构造函数constexpr,则对b的初始constexpr赋值将失败。看来,constexpr仅在您分配给声明为constexpr的变量时生效,否则它似乎只是作为编译器的顾问,可以通过静态求值优化代码路径,但如果您没有显式指示它使用constexpr变量分配,则不能保证这样做
也就是说,在正常情况下,声明构造函数constexpr似乎没有任何影响。以下机器代码是通过以下命令行生成的:
g++ -std=c++11 -Wall -g -c main.cpp -o obj/Debug/main.o
g++ -o bin/Debug/TestProject obj/Debug/main.o
所以你的b分配产生了这个代码:
0x4005bd push rbp
0x4005be mov rbp,rsp
0x4005c1 mov DWORD PTR [rbp-0x4],0x1
0x4005c8 mov eax,0x0
0x4005cd pop rbp
0x4005ce ret
但是,如果删除b变量上的constexpr声明:
0x4005bd push rbp
0x4005be mov rbp,rsp
0x4005c1 sub rsp,0x10
0x4005c5 lea rax,[rbp-0x5]
0x4005c9 mov rdi,rax
0x4005cc call 0x4005ee <test::test()>
0x4005d1 lea rdx,[rbp-0x5]
0x4005d5 lea rax,[rbp-0x6]
0x4005d9 mov rsi,rdx
0x4005dc mov rdi,rax
0x4005df call 0x4005f8 <test::operator+(test const&) const>
0x4005e4 mov DWORD PTR [rbp-0x4],eax
0x4005e7 mov eax,0x0
0x4005ec leave
0x4005ed ret
0x4005bd推送rbp
0x4005be mov rbp,rsp
0x4005c1子rsp,0x10
0x4005c5 lea rax,[rbp-0x5]
0x4005c9 mov rdi,rax
0x4005cc调用0x4005ee
0x4005d1 lea rdx,[rbp-0x5]
0x4005d5 lea rax,[rbp-0x6]
0x4005d9移动rsi,rdx
0x4005dc mov rdi,rax
0x4005df调用0x4005f8
0x4005e4 mov DWORD PTR[rbp-0x4],eax
0x4005e7 mov eax,0x0
0x4005ec离开
0x4005ed ret
< P>它看起来好像操作符和构造函数没有声明为CONTXPR,但是这是一个你应该参考编译器的细节的情况,的确。 < P>在C++标准中可以读取<代码> CONTXPRP</代码>构造函数对类类型的影响。
3.9类型
(……)
如果类型是:
- 它是聚合类型(8.5.1)或至少有一个constexpr构造函数或不是复制或移动构造函数的构造函数模板
(……)
因此,constexpr
构造函数意味着可以执行静态初始化,并且可以使用如下方法:
#include <iostream>
struct test {
int val;
constexpr test(int val) : val(val) { }
};
template<int N>
struct CC {
double m[N];
};
int main()
{
CC<test(6).val> k; // usage where compile time constant is required
std::cout << std::end(k.m) - std::begin(k.m) << std::endl;
return 0;
}
在上面的示例中,实例a
未声明为常量,因此即使a
可以是constexpr
常量,但它不是一个常量(因此可以修改)。拥有constexpr构造函数不会自动声明该变量constexpr,因此t
不是constexpr。在本例中,您正在调用一个constexpr函数,这一行:
constexpr int b = t+test();
可按以下方式查看:
constexpr int b = t.operator+( test() );
因此,问题是test()
是否是一个常量表达式,这是因为构造函数是constepr,不属于草案C++11标准节5.19
[expr.const]段落2
中的任何例外情况,该段落说:
条件表达式是核心常量表达式,除非
作为可能的求值子表达式涉及以下内容之一
[……]
并包括以下项目符号:
- 对文本类或constexpr函数调用除constexpr构造函数以外的函数[注:重载解析]
(13.3)照常使用-尾注]李>
[……]
- 对constexpr构造函数的一种调用,其参数在被函数调用替换时
替换(7.1.5),不要为构造函数调用和
mem初始值设定项中的完整表达式
- 对constexpr函数或constexpr构造函数的调用将超过定义的实现
递归极限(见附录B)
通过引入成员变量x
,对test
进行一些小的更改,我们可以更容易地看到这一点:
class test{
public:
constexpr test(){
}
constexpr int operator+(const test& rhs) const {
return x + 1 ;
}
int x = 10 ;
};
试图在operator+
中访问它,我们可以看到下面的行现在失败了:
constexpr int b = t+test();
clang()中出现以下错误:
它失败是因为t
不是constexpr变量,因此它的子对象也不是constexpr变量
你的第二个例子:
constexpr int c = w + 2;
不起作用,因为它属于草案C++11标准部分5.19
[expr.const]中的一个例外:
- 左值到右值的转换(4.1),除非应用于
[……]
- 一种整型或枚举型glvalue,它引用具有先前初始化的非易失性常量对象,已初始化
使用常量表达式,或
如果您编写了一个实例不是constexpr的上下文,那么您的类就不会被用作文本类。有关详细信息,请参见下面的回答您的运算符+
不访问其操作数的值(或者,在语言律师看来,它不执行“左值对右值的转换”
error: constexpr variable 'b' must be initialized by a constant expression
constexpr int b = t+test(); // works at compile time!
^ ~~~~~~~~
note: read of non-constexpr variable 't' is not allowed in a constant expression
return x + 1 ;
^
constexpr int c = w + 2;