C++结构体和类的位运算

3
我正在开发一个通用的遗传算法库,其中每个生物的染色体是其在内存中的位表示。例如,如果我想突变一个生物,我会随机地翻转对象本身的位。
起初,我尝试使用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' 的全局运算符(或没有可接受的转换) GeneticAlgorithm path\geneticalgorithm\geneticalgorithm\BitManip.hpp 57
如果我纠正这个错误,那么其他运算符也会出现问题。
我以前没有在我的代码中使用过位运算符,所以我猜这些运算符不能与任何对象一起使用?如果是这样,我该如何解决这个问题?

3
在 C++ 标准定义的几乎所有位运算符中,它们都以“该运算符仅适用于整型或未作用域的枚举操作数”结尾描述。 - Cory Kramer
4
C++和C是不同的编程语言。在你的问题中不要标记C,特别是因为你有模板和C++标准库类。 - Cory Kramer
1
你可以将生物体或其他任何东西的地址转换为char*,计算其中涉及的字节和位偏移量,并反转该字节中的_那个_位。 - Peter - Reinstate Monica
2
@FelipeCanever:“我将C作为标签,因为我的问题更多涉及位运算,而不是模板和标准库。”它们仍然是不同的语言。你不能假设在C中成立的所有东西也在C++中成立。操作符在这两种语言中并不总是表现相同。 - cdhowie
如果您的结构体中有指针成员(或隐式成员,例如它是多态类型),那么@Peter建议的方法会导致非常悲惨的结果。除此之外,它应该能够运行(除了在技术上通过操作填充位等来调用UB)。 - Oliver Charlesworth
显示剩余4条评论
2个回答

2
您想要实现的可以像这样完成(请参阅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重新解释为一组字节(unsigned char)数组,然后确定该翻转哪个字节(i/8),以及字节内的哪个位(i%8)。

注意:此外,在函数开头添加以下内容可能是安全的:

assert(i < sizeof(T)*8)

1
我觉得你还没有完全领会C++提供的面向对象特性。(对于那些从以数据为中心的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类。谢谢。 - Felipe Canever

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接