如果类型(不)相等,C++ 是否可以有条件地编译代码

Is it possible in C++ to conditionally compile code if types are (not) equal

目前我遇到了从 64 位(向后)到 32 位的代码可移植性问题。问题是,class 的方法在 64 位平台上重载,与 32 位平台上的另一个重载冲突。

两种方法如下所示:

void myfunc(unsigned o);
void myfunc(size_t o);

在 32 位架构上,它们看起来与编译器相同并会抛出一些错误。

所以,问题是,是否可以这样做:

void myfunc(unsigned o);
#if typeid(unsigned) != typeid(size_t)
void myfunc(size_t o);
#endif

我目前的解决方案是这样的:

void myfunc(unsigned o);
#if __WORDSIZE == 64
void myfunc(size_t o);
#endif

但是还有一些不好的感觉,WORDSIZE 不是最合适的,因为它并不一定意味着类型不一样。

编辑:好的,这是第 705 行和第 706 行有问题的地方,它们在 32 位 arm 上编译时会产生错误。 https://github.com/ceph/ceph/blob/master/src/include/buffer.h#L705

这可能是使用模板的一个选项:

#include <iostream>

class A {
public:
    template<typename T>
    std::enable_if_t<std::is_same<T, int>::value || 
    std::is_same<T, unsigned>::value ||
    std::is_same<T, std::size_t>::value >
    advance(T o) {
        std::cout << "s" << std::endl;
        A::advance<unsigned>(static_cast<unsigned>(o));
    }
};

template<>
    void A::advance(int o) = delete;

template<>
    void A::advance(unsigned o) {
        std::cout << "u" << std::endl;
    }

int main()
{
  A a;

  unsigned x;
  std::size_t y;
  int z;
  char p;

  a.advance(x);
  a.advance(y);
  //a.advance(z);
  //a.advance(p);

  return 0;
}

如果底层 C 标准库支持 "Floating-point extensions part 1" (ISO/IEC TS 18661-1:2014),那么您可以使用预处理器宏来识别类型的大小:

#include<climits>
#include<cstdint>

void myfunc(unsigned o);
#if UINT_WIDTH != SIZE_WIDTH
void myfunc(size_t o);
#endif

这是支持的,例如通过 glibc。请注意,如果未定义宏,即如果未实现规范,则测试总是会失败,因此您也应该检查一下,即

#if UINT_WIDTH != SIZE_WIDTH || !defined(UINT_WIDTH) || !defined(SIZE_WIDTH)

如果没有这样的实现定义的宏,预处理器就不能用来实现你想要的,因为它实际上并不知道 C 或 C++ 类型。

C++ 编译级别的任何解决方案都需要您至少在一定程度上修改函数声明。

我不认为这个解决方案特别干净,但您当前的解决方案也不是。真的,如果正如我所怀疑的那样,目标是避免某些隐式转换,该方法应该是一个带有 static_assert 适当限制类型的模板。


编辑:

上面的代码与当前的 glibc 和 gcc 一样工作,但我不确定从技术上讲这是否是正确的行为。这是扩展 C11 而非 C++ 的技术规范。我不知道 C++ 如何或是否包含这些,或者它们是否会被视为实现定义的扩展。

此外,根据规范,只有在您

时才应定义宏
#define __STDC_WANT_IEC_60559_BFP_EXT__

在第一个 #include<stdint.h>#include<limits.h> 之前。当使用 glibc 在 C 模式下编译时,GCC 实际上需要这个。

可以通过比较宏 __STDC_IEC_60559_BFP__201ymmL 来检查规范是否已实现。然而,带有 glibc 的 GCC 似乎没有设置这个宏,并且文档指出对规范的支持只是部分的。

在相信上述比较之前,您可能至少应该确保 UINT_WIDTHSIZE_WIDTH 已设置。如果不是,例如因为不支持规范,所以将始终评估为 0 != 0,即 false.

你可以使用 std::enable_if

#include <type_traits>

struct S {
      void advance(unsigned o);
      std::enable_if<!std::is_same<unsigned, std::size_t>::value>::type
      advance(std::size_t o) { advance(static_cast<unsigned>(o)); }
};

不过,正如其他人已经指出的那样,我会放弃 unsigned 变体,只保留 std::size_t 变体。