如果类型(不)相等,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_WIDTH
和 SIZE_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
变体。
目前我遇到了从 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_WIDTH
和 SIZE_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
变体。