C++ 在编译时检查三向比较运算符支持
我希望在代码中有条件地启用C++ 在编译时检查三向比较运算符支持,c++,c++14,portability,c++20,spaceship-operator,C++,C++14,Portability,C++20,Spaceship Operator,我希望在代码中有条件地启用操作符重载,这取决于当前版本的编译器及其命令行选项是否支持它。例如,我希望将以下代码编译为C++14、17和20(这本质上是我之前提出的问题的续篇): \define SPACESHIP\u操作符\u受支持1//(const thing&)const{return false;} bool运算符=(const thing&)const{return true;} int运算符-(const thing&)const{return 0;} //但明确删除不同N的ops:
操作符
重载,这取决于当前版本的编译器及其命令行选项是否支持它。例如,我希望将以下代码编译为C++14、17和20(这本质上是我之前提出的问题的续篇):
\define SPACESHIP\u操作符\u受支持1//(const thing&)const{return false;}
bool运算符=(const thing&)const{return true;}
int运算符-(const thing&)const{return 0;}
//但明确删除不同N的ops:
//(见https://stackoverflow.com/questions/65468069)
模板布尔运算符==(常量对象&)常量=删除;
模板布尔运算符!=(const thing&)const=delete;
模板布尔运算符<(const thing&)const=delete;
模板布尔运算符>(const thing&)const=delete;
模板布尔运算符=(const thing&)const=delete;
模板int运算符-(const thing&)const=delete;
//但如果我不删除不同的模板参数,那么
//like thing()thing()将被允许编译,因为
//被隐式转换为int。因此我*必须*在受支持时删除它。
#如果支持太空船操作器
std::strong_排序运算符(const thing&)const=default;
模板std::强排序运算符(const thing&)const=delete;
#恩迪夫
};
int main(){
事物t0;
事物t1;
(void)(t0==t0);//第39行
//(void)(t0==t1);//第40行
#如果支持太空船操作器
(无效)(t0 t0);//第42行
//(void)(t0 t1);//第43行
#恩迪夫
}
因此,首先,请快速解释一下:
- 隐式
是一项要求运算符int
- 比较运算符仅为具有相同
的N
对象定义
- 必须显式删除不匹配的
s的运算符,否则编译器将决定将N
隐式应用于两侧,并使用运算符int
比较()int
- 预期的行为是第40行和第43行(已标记)无法编译
- 代码需要编译为C++14、17和20
- 如果我根本没有重载
,那么像
这样的东西就被错误地允许编译(由于隐式转换为thing()thing()
;与其他操作符的情况相同)。换句话说:默认的int
并不是在所有情况下都适用,所以我不能任由其发展操作符
- 如果我总是同时编写
重载,那么程序将无法编译为C++14和C++17,或者可能无法在C++20实现不完整的编译器上编译(尽管我没有遇到这种情况)
SPACESHIP\u OPERATOR\u受支持,上面的代码就满足所有要求,但我希望它是自动的
因此,我的问题是:是否有一种方法可以在编译时检测对操作符的支持,并在存在的情况下有条件地启用代码?或者是否有其他方法可以让C++14到20实现这一点?
我有预编译的想法,但如果有一些神奇的模板解决方案,那也行。我真的想要一个独立于编译器的解决方案,但至少我希望它能在GCC(5.x及更高版本)和MSVC(理想情况下是2015及更高版本)上运行。这就是功能测试宏的用途。有一个定义所有宏及其值的函数;这些是您检查的宏和值,所有供应商都同意遵守这些宏和值
特别是三方比较有点棘手,因为这是一个需要语言和库支持的特性。有一个语言级功能测试宏,但它不是为您(用户)设计的,而是为标准库作者设计的,有条件地提供该功能
所以你真正要做的是:
#如果有包含()
#包括
#如果定义了(uuu cpp_lib_三向比较)&&&&(uuu cpp_lib_三向比较)>=201907
#定义宇宙飞船\u运算符\u是否受\u支持1
#恩迪夫
#恩迪夫
现在,在代码的其余部分,您可以检查\ifdef SPACESHIP\u OPERATOR\u是否受支持,以有条件地提供
:
#支持ifdef SPACESHIP_运算符_
布尔运算符==(常量对象&)常量=默认值;
std::strong_排序运算符(const thing&)const=default;
模板布尔运算符==(常量对象&)常量=删除;
模板std::强排序运算符(const thing&)const=delete;
#否则
布尔运算符==(常数事物&)常数{返回真;}
接线员=(const thing&)const{return false;}
布尔运算符<(const thing&)const{return false;}
bool操作符>(const thing&)const{return false;}
bool运算符=(const thing&)const{return true;}
模板布尔运算符==(常量对象&)常量=删除;
模板布尔运算符=(const thing&)const=delete;
模板布尔运算符<(const thing&)const=delete;
模板布尔运算符>(const thing&)const=delete;
模板布尔运算符=(const thing&)const=delete;
#恩迪夫
您不需要同时提供默认的
和所有关系运算符。这就是为什么我们有
:所以你可以自己写
。您仍然需要提供运算符==
,但这仅仅是因为您正在做一些特殊的事情,需要删除
@HTNW是的,谢谢。@JasonC好的,是的,因为您正在删除,所以需要特别地默认和删除相等项。更新。@Barry作为一个附带问题:是否可以安全地假设当且仅当宇宙飞船操作员
#define SPACESHIP_OPERATOR_IS_SUPPORTED 1 // <--- i want this to be automatic
#if SPACESHIP_OPERATOR_IS_SUPPORTED
#include <compare>
#endif
template <int N> struct thing {
// assume an implicit conversion to a "math-able" type exists:
operator int () const { return 0; }
// define a set of comparison operators for same N on rhs:
bool operator == (const thing<N> &) const { return true; }
bool operator != (const thing<N> &) const { return false; }
bool operator < (const thing<N> &) const { return false; }
bool operator > (const thing<N> &) const { return false; }
bool operator <= (const thing<N> &) const { return true; }
bool operator >= (const thing<N> &) const { return true; }
int operator - (const thing<N> &) const { return 0; }
// but explicitly delete ops for different N:
// (see https://stackoverflow.com/questions/65468069)
template <int R> bool operator == (const thing<R> &) const = delete;
template <int R> bool operator != (const thing<R> &) const = delete;
template <int R> bool operator < (const thing<R> &) const = delete;
template <int R> bool operator > (const thing<R> &) const = delete;
template <int R> bool operator <= (const thing<R> &) const = delete;
template <int R> bool operator >= (const thing<R> &) const = delete;
template <int R> int operator - (const thing<R> &) const = delete;
// but if i don't delete <=> for differing template parameters then things
// like thing<0>() <=> thing<1>() will be allowed to compile because they'll
// be implicitly converted to an int. so i *have* to delete it when supported.
#if SPACESHIP_OPERATOR_IS_SUPPORTED
std::strong_ordering operator <=> (const thing<N> &) const = default;
template <int R> std::strong_ordering operator <=> (const thing<R> &) const = delete;
#endif
};
int main () {
thing<0> t0;
thing<1> t1;
(void)(t0 == t0); // line 39
//(void)(t0 == t1); // line 40
#if SPACESHIP_OPERATOR_IS_SUPPORTED
(void)(t0 <=> t0); // line 42
//(void)(t0 <=> t1); // line 43
#endif
}