【问题标题】:C++ bitwise operations on structs and classes结构和类的 C++ 位运算
【发布时间】:2015-10-02 05:14:44
【问题描述】:

我正在开发一个通用遗传算法库,其中每个生物体的染色体是它在内存中的位表示。因此,例如,如果我想改变一个有机体,我会随机翻转对象本身的位。

起初,我尝试使用 C++ 标准库中的 bitset 类,但是,当转换回对象 T 时,我唯一的选择是使用 to_ullong 成员函数,这对于表示来说是个问题位数大于unsigned long long 的大小。

然后我决定为任何对象T 上的按位操作创建一个通用库,这样我就可以将这些操作直接应用于对象本身,而不是先将它们转换为bitset

所以你可以看到我想要实现的目标,这是库中的一个函数:

template<typename T>
void flip(T& x, size_t const i)
{
    x ^= 1 << i;
}

它在 GA 库中是这样使用的:

template<typename T>
void GeneticAlgorithm<T>::mutate(T& organism, double const rate)
{
    std::random_device rd;
    std::mt19937 mt(rd());

    std::uniform_real_distribution<double> dist(0, 1);

    for(size_t i = 0; i < m_nBits; ++i)
        if(dist(mt) <= rate)
            bit::flip(organism, i);
}

如果这能正常工作,那就太好了,但是现在我从 VC++ 2015 RC 编译器收到此错误消息:

严重性代码描述项目文件行错误 C2677 二进制“^”:否 找到了类型为“T”的全局运算符(或者没有可接受的 转换)遗传算法路径\遗传算法\遗传算法\BitManip.hpp 57

如果我为 ^ 更正此错误,我会为其他运算符获得更多信息。

我之前没有在我的代码中使用过位运算符,所以我猜这些运算符不应该与任何对象一起使用?如果是这样,我该如何解决这个问题?

【问题讨论】:

  • 在 C++ 标准中定义的几乎所有bitwise operators 中,都以“运算符仅适用于整数或无范围枚举操作数”结束描述
  • C++ 和 C 也是不同的语言。不要在你的问题中标记 C,特别是因为你有模板和 C++ 标准库类。
  • 您总是可以将有机体的地址或其他任何东西转换为 char* ,计算相关字节和其中的位偏移量,然后将 that 中的 that 位反转 字节。
  • 我的直觉也是,flip() 应该是有机体的成员函数。也许您将来会使用与比特不同的东西(假设您想计算每个基因发生的翻转)。您将想要在不确切知道有机体如何处理它的情况下执行翻转。见鬼,你可能会调用一个人口众多的远程过程。
  • @FelipeCanever “我把 C 作为标签是因为我的问题与模板和标准库的按位运算更相关。” 它们仍然是不同的语言。您不能假设在 C 中适用的所有内容在 C++ 中也适用。运算符在两种语言中的行为并不总是相同。

标签: c++ bit-manipulation bit genetic-algorithm


【解决方案1】:

您想要实现的目标可以这样完成(参见 Peter Schneider 的评论):

template<typename T> void flip(T& x, size_t const i) {
    unsigned char* data = reinterpret_cast<unsigned char*>(&x);
    data[i/8] ^= (1 << (i%8));
}

它所做的是将数据 x 重新解释为字节数组(无符号字符),然后确定应该翻转哪个字节 (i/8),然后确定字节中的哪个位 (i%8)。

注意:另外,在函数开头添加可能是安全的:

assert(i < sizeof(T)*8)

【讨论】:

    【解决方案2】:

    我的印象是您还没有完全理解 C++ 提供的面向对象的特性。 (这在 C 中以数据为中心的编程中并不罕见。C++ 专门设计用于以所需的速度进行转换并使其无痛。)

    我的建议是将翻转操作封装在一个有机体中,让有机体来处理。作为插图(未经测试,但可以编译):

    #include<climits>  // CHAR_BIT
    #include<cstdlib>  // exit()
    
    class string;
    void log(const char *);
    
    // inaccessible from the outside
    constexpr int NUM_TRAITS = 1000;
    constexpr size_t TRAIT_ARR_SZ = (NUM_TRAITS+CHAR_BIT-1)/CHAR_BIT;
    
    class Organism
    { 
    
        char traits[TRAIT_ARR_SZ];
        int flips[NUM_TRAITS];
    
        /////////////////////////////////////////////////////////////
        public:
    
        Organism()  {  /* set traits and flips zero */  }
    
        // Consider a virtual function if you may derive 
        /** Invert the trait at index traitIndex */
        void flipTrait(int traitIndex)
        {
            if( traitIndex >= NUM_TRAITS ) { log("trait overflow"); exit(1); }
    
            int charInd = traitIndex / CHAR_BIT;
            int bitInd = traitIndex % CHAR_BIT;
    
            traits[traitIndex] ^= 1 << bitInd;
            flips[traitIndex]++;
        }
    
        // Organisms can do so much more!
        void display();
        void store(string &path);
        void load(string &path);
        void mutate(float traitRatio);
        Organism clone();
    };
    

    【讨论】:

    • 我几乎总是只使用 C++ 进行编程,因此我决定制作一个通用函数库,以便在结构中快速构建 GA 原型。既然您已经指出,我想我将添加一个可以继承的 Organism 类。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    相关资源
    最近更新 更多