C++ 如何使用traits访问编译时常量值?
我正在从事一个项目,其中某个函数的行为需要在几个值之间切换:C++ 如何使用traits访问编译时常量值?,c++,templates,C++,Templates,我正在从事一个项目,其中某个函数的行为需要在几个值之间切换: class James{ public: James(){ if(a==0){ //do this }else{ // do that } } }; 当前,在运行时从配置文件中读取“a”。然而,实际上,“a”可以在编译时确定,而不是在运行时确定。我在考虑上一堂特质课 struct TraitZero{ conste
class James{
public:
James(){
if(a==0){
//do this
}else{
// do that
}
}
};
当前,在运行时从配置文件中读取“a”。然而,实际上,“a”可以在编译时确定,而不是在运行时确定。我在考虑上一堂特质课
struct TraitZero{
constexpr int a = 0;
};
struct TraitOne{
constexpr int a = 1;
};
然后将James转换为模板类
template<typename Trait>
class James{
constexpr int a = Trait::a;
public:
James(){
if(a=0){
//do this
}else{
// do that
}
}
};
模板
詹姆斯班{
constexpr int a=Trait::a;
公众:
詹姆斯(){
如果(a=0){
//这样做
}否则{
//那样做
}
}
};
我不知道我哪里弄错了,但这没有编译
我想知道这里是否有人曾经解决过这样的问题。任何人都可以分享一些见解吗?为什么不在编译时使用-D选项传递a#define?
例如:
a
数据成员必须声明为constepr
和静态
,才能以您尝试使用它们的方式使用:
struct TraitZero{
static constexpr int a = 0;
};
struct TraitOne{
static constexpr int a = 1;
};
抛开它目前的格式不正确这一事实不谈,否则您将不能作为Traits::a
访问它。这同样适用于类
James
:
template<typename Trait>
class James{
static constexpr int a = Trait::a;
//...
};
即使您被允许修改a
(并且您不是因为它是静态constexpr数据成员),在这种情况下,您也会将0分配给a
,并不断获得else
分支。很可能您正在寻找相似但略有不同的东西:
if(a == 0){
下面是一个基于您的代码修复后的示例:
#include<iostream>
struct TraitZero{
static constexpr int a = 0;
};
struct TraitOne{
static constexpr int a = 1;
};
template<typename Trait>
class James{
static constexpr int a = Trait::a;
public:
James(){
if(a==0){
std::cout << "0" << std::endl;
}else{
std::cout << "1" << std::endl;
}
}
};
int main() {
James<TraitZero> j0;
James<TraitOne> j1;
}
#包括
struct TraitZero{
静态constexpr int a=0;
};
结构TraitOne{
静态constexpr int a=1;
};
模板
詹姆斯班{
静态constexpr int a=Trait::a;
公众:
詹姆斯(){
如果(a==0){
std::coutAs,只有静态
数据成员可以是constepr
,您需要在条件中使用==
而不是=
这就是说,由于您希望在编译时确定a
,因此在编译时基于a
进行分支也可能对您有益。为此,您可以使用or(从C++17开始)
假设有以下三个特点
struct TraitZero{
static constexpr int a = 0;
};
struct TraitOne{
static constexpr int a = 1;
};
template<size_t N>
struct TraitN {
static constexpr int a = N;
};
从这里可以看出,constexpr if可以用来创建比SFINAE更干净、更自然的代码,其优点是它仍然会在编译时而不是运行时进行评估;不幸的是,大多数编译器还不支持它。[在这个特定的例子中,James()的每个版本
也将缩短一条机器指令(使用GCC 7.0编译时),因为没有使用伪参数来区分重载。]
更具体地说,对于constexpr if,如果条件为true
,则放弃语句false,如果条件为false
,则放弃语句true;实际上,这基本上意味着编译器将整个constexpr if语句视为要执行的分支。例如,在这种情况下,编译器将根据Trait::a
的值,选择以下三个函数之一
// If Trait::a == 0:
James() {
std::cout << "Trait::a is 0.\n";
}
// If Trait::a == 1:
James() {
std::cout << "Trait::a is 1.\n";
}
// If Trait::a == anything else:
James() {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
每个类型的构造函数都将被专门编码以输出适当的行,并且三个构造函数中没有一个实际包含任何分支
请注意,出于个人喜好,我只将成员a
标记为不必要;因为我可以直接访问Trait::a
,所以我更愿意这样做,所以如果我放弃了,我就不必检查a
是什么。如果你愿意,或者其他地方需要,请随意使用它。a=0
应该是a==0
你知道吗意思是a==0?结构中不需要constexpr int a
是静态的
?是否愿意分享编译错误?顺便说一句,你不必使用traits类。你可以简单地直接在整数上模板James
。直接在布尔或枚举上模板是很常见的,特别是为了控制行为。如果你这样做的话nk你需要更多的东西一个traits类很好,但是如果你只需要一两个编译时布尔值,YAGNI.yes,==而不是=,我现在被CLion宠坏了
template<typename Trait>
class James {
// Unnecessary, we can access Trait::a directly.
//static constexpr int a = Trait::a;
public:
template<bool AZero = Trait::a == 0>
James(std::enable_if_t<AZero, unsigned> = 0) {
std::cout << "Trait::a is 0.\n";
}
template<bool AOne = Trait::a == 1>
James(std::enable_if_t<AOne, int> = 0) {
std::cout << "Trait::a is 1.\n";
}
template<bool ANeither = (Trait::a != 0) && (Trait::a != 1)>
James(std::enable_if_t<ANeither, long> = 0) {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
};
template<typename Trait>
class James {
// Unnecessary, we can access Trait::a directly.
//static constexpr int a = Trait::a;
public:
James() {
if constexpr (Trait::a == 0) {
std::cout << "Trait::a is 0.\n";
} else if constexpr (Trait::a == 1) {
std::cout << "Trait::a is 1.\n";
} else {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
}
};
// If Trait::a == 0:
James() {
std::cout << "Trait::a is 0.\n";
}
// If Trait::a == 1:
James() {
std::cout << "Trait::a is 1.\n";
}
// If Trait::a == anything else:
James() {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
int main() {
James<TraitZero> j0;
James<TraitOne> j1;
James<TraitN<2>> j2;
}
Trait::a is 0.
Trait::a is 1.
Trait::a is neither 0 nor 1.