2017-03-03 36 views
1

我工作的一个项目中某一功能的行为需要几个值之间切换:如何使用traits来访问编译时const值?

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; 
}; 

然后把詹姆斯变成一个模板类

template<typename Trait> 
class James{ 
    constexpr int a = Trait::a; 
    public: 
     James(){ 
      if(a=0){ 
       //do this 
      }else{ 
       // do that 
      } 
     } 
    }; 

我不知道我在哪里弄错了,但这并不编译。

我想知道这里有没有人曾经遇到这样的问题。谁能分享一些见解?

+1

'A = 0'应该是'一个== 0' – Barry

+0

您是不是要找一个== 0? –

+1

在你的结构中''constexpr int a''是否需要是'static'?小心分享编译错误? – qxz

回答

1

a数据成员必须被声明为constexprstatic使用您要使用它们的方式:

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; 
    //... 
}; 

还要注意,可能下面是不是你想要的东西:

if(a=0){ 

即使你被允许修改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; 
} 
+0

是的,我想要的是==而不是=。谢谢 –

1

由于has already been mentioned by skypjack,只有static数据成员,也为constexpr,并且您需要在条件中使用==而不是=

也就是说,既然你想在编译时确定a,那么你也可以在编译时根据a进行分支。为此,您可以使用SFINAE或(自C++ 17起)constexpr if


假设以下三个特征...

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; 
}; 

我们能够做到这一点的?

  • SFINAE:

    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"; 
        } 
    }; 
    

    这样做是有条件地选择基础上的Traits::a价值James()的版本之一,使用虚拟参数来启用重载;这对构造函数和析构函数以外的函数更简单,因为enable_if可以用于它们的返回类型。

    注意使用模板参数,而不是直接在enable_if中自己检查Trait::a。由于SFINAE只能在函数的上下文中使用类型和表达式来执行,因此可以用它们“拖入”,可以这么说;我喜欢在这样做的时候执行逻辑,因为它最大限度地减少了enable_if的侵入性。

  • constexpr如果:

    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"; 
         } 
        } 
    }; 
    

    正如这里可以看到,constexpr如果可以用来创造出比SFINAE更清洁,更自然的代码,其优点是它仍然会在编译的时候,而不是进行评估运行;不幸的是,它还没有得到大多数编译器的支持。 [在此特定情况下,James()每个版本也将是一个机器指令短(当与GCC 7.0编译),由于不使用虚设参数重载之间进行区分来。]

    更具体地,constexpr如果,如果条件是true,如果它的false语句真被丢弃的语句假被丢弃;实际上,这基本上意味着编译器将整个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"; 
    } 
    

在任一情况下,用下面的代码...产生

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. 

每种类型的构造将是编码专门输出适当的行,三个构造函数都不会包含任何分支。

注意,我只标记构件a不必要出个人喜好的;因为我可以直接访问Trait::a,所以我宁愿这么做,所以如果我曾经放弃过,我不需要检查什么是a。随意使用它,如果你想,或者如果它需要在别处。