C++ 模板是编译时构造。在 C++20 中,模板函数怎么可能用非静态对象实例化?
struct X {};
template<typename T, X x> auto f() {
return x;
}
int main() {
auto x = X{};
f<double, x>();
}
参数必须在编译时完全可用。
如果类型
X
中有非静态数据成员,则上述示例不可用:
struct X {
int value; // <<==== added this
};
template<typename T, X x> auto f() {
return x;
}
int main() {
auto x = X{};
f<double, x>();
}
要投诉了
<source>:8:17: error: the value of 'x' is not usable in a constant expression
8 | f<double, x>();
| ~~~~~~~~~~~~^~
<source>:7:10: note: 'x' was not declared 'constexpr'
7 | auto x = X{};
| ^
要解决这个问题,您必须将
x
声明为 constexpr
struct X {
int value;
};
template<typename T, X x> auto f() {
return x;
}
int main() {
constexpr auto x = X{3};
f<double, x>();
}
https://godbolt.org/z/f56nb1Ped
现在想象一下,这类似于传递一个整数作为模板参数。
template< size_t N >
struct CharT {
char data[N];
};
它是否是静态的并不重要,重要的是它是否在编译时可用。
x
由于其定义方式,因此在编译时可用。您可以在 constexpr
下阅读更多相关信息(对于函数,consteval
)。
注意
x
变量本身 not 什么成为模板参数。模板参数被声明为一个值,所以f<double, x>()
实际上copys(在编译时)x
形成模板参数的值。也就是说,作为模板参数的转换表达式包括通过聚合初始化的copy。这与调用原型函数 void f(X x)
相同。 C++ 具有值语义!
在评估
foo<double, x>()
中的模板参数时,不允许从x
中读取。 (更准确地说,
x
是类类型,“读取”没有意义;读取标量子对象是非法的——“原语”,如
int
s、
float
s 等,在
X
中的字段。)但是,在这种情况下,
X
中没有字段,因此复制
x
以形成模板参数不需要读取
x
。所以不需要发生任何违法行为! foo<double, x>()
编译成功,因为模板参数值可以在不做任何非常量的情况下确定。 (特别注意,在常量表达式中允许形成对非
constexpr
变量的引用。) 注意以下内容也可以编译。同样,重要的是
foo<double, x>
要求
x
被copied。在这里,用户指定的复制构造函数实际上并没有读取
x
,因此代码可以编译。但是默认的复制构造函数将从
x
读取,并且使用该构造函数会使代码无法编译。
struct X {
int i;
constexpr X() : i(0) { }
constexpr X(X const&) : X() { } // (1) does not read source object!
// if (1) is commented out, the call to foo will fail to compile
// the compiler-generated copy constructor if (1) is commented out is effectively
// constexpr X(X const &x) : i(x.i) { }
};
int main() {
X x;
foo<double, x>();
// ^ as a converted expressions *includes a call to the copy constructor*, and that call is indeed a constant expression!
}
最后,请注意 static
只需要在常量表达式中获取地址/形成对对象的引用。查看对象的value并不要求它是静态的。