仅针对二进制运算符防止隐式转换运算符 [英] Preventing implicit conversion operator only for binary operators
问题描述
我遇到一个问题,可以归结为以下问题:即使应该失败,也会编译 ==
运算符用法(C ++ 17,已在GCC 5上进行了测试).x,8.x和9.x):
I'm having an issue that I've boiled down to the following, where an ==
operator usage compiles even though it's supposed to fail (C++17, tested on GCC 5.x, 8.x, and 9.x):
template <int N> struct thing {
operator const char * () const { return nullptr; }
bool operator == (const thing<N> &) const { return false; }
};
int main () {
thing<0> a;
thing<1> b;
a == b; // i don't want this to compile, but it does
}
之所以进行编译,是因为编译器选择执行此操作:
The reason it is compiling is because the compiler is choosing to do this:
(const char *)a == (const char *)b;
代替此:
// bool thing<0>::operator == (const thing<0> &) const
a.operator == (b);
因为后者不匹配,因为 b
是 things <1>
而不是 thing <0>
.(顺便说一句,当使用原始比较运算符时,它还会产生 unused-comparison
警告;这并不有趣,但这就是为什么出现这些警告的原因.)
Because the latter isn't a match, since b
is a thing<1>
not a thing<0>
. (Incidentally, it also produces unused-comparison
warnings when the primitive comparison operator is used; not interesting, but that's why those warnings appear.)
我已经在 C ++见解上对此进行了验证(实际上,这就是我发现原因的方式),其输出为:
I've verified this (actually it's how I discovered the cause) on C++ Insights, which outputs:
template <int N> struct thing {
operator const char * () const { return nullptr; }
bool operator == (const thing<N> &) const { return false; }
};
/* First instantiated from: insights.cpp:7 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct thing<0>
{
using retType_2_7 = const char *;
inline operator retType_2_7 () const
{
return nullptr;
}
inline bool operator==(const thing<0> &) const;
// inline constexpr thing() noexcept = default;
};
#endif
/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct thing<1>
{
using retType_2_7 = const char *;
inline operator retType_2_7 () const
{
return nullptr;
}
inline bool operator==(const thing<1> &) const;
// inline constexpr thing() noexcept = default;
};
#endif
int main()
{
thing<0> a = thing<0>();
thing<1> b = thing<1>();
static_cast<const char *>(a.operator const char *()) == static_cast<const char *>(b.operator const char *());
}
在 main
的最后一行中显示转换运算符的用法.
Showing the conversion operator usage in that last line of main
.
我的问题是:当然,如果我明确指定了转换运算符,那么整个程序就可以正常工作,但是我真的很想保持隐式转换运算符和强制编译器执行正确使用 operator ==
(其中,"correct" =如果参数类型不同,则无法编译).这可能吗?
My question is: Of course, the whole thing behaves properly if I make the conversion operator explicit, but I'd really like to keep the implicit conversion operator and have the compiler enforce correct usage of operator ==
(where "correct" = failing to compile if the parameter type is different). Is this possible?
请注意,拥有 things< N>对我来说不不重要== const char *
工作.也就是说,我不需要此重载:
Note that it is not important to me to have thing<N> == const char *
work. That is, I don't need this overload:
bool thing<N>::operator == (const char *) const
所以我不在乎解决方案是否破坏了 ==
的味道.
So I don't care if a solution breaks that flavor of ==
.
我确实在这里搜索了其他帖子;有些数字具有令人误解的相似标题,但最终却毫无关联.看起来此帖子存在与相关的问题(不需要的隐式转换),但仍然不适用.
I did search through other posts here; there were a number with misleadingly similar titles that ended up being unrelated. It looks like this post had a related problem (undesired implicit conversions) but it's still not applicable.
为了完整起见,这是我实际所做的工作的最小限度但更具代表性的示例,其目的是允许 ==
用于 thing< T,N>;
和 thing< R,N>
,即 N
必须相同,但第一个模板参数可以不同.我将其包括在内,以防它影响可能的解决方案,因为这是我真正需要的正确行为:
For completeness, here is a slightly less minimal but more representative example of what I'm actually doing, where the intent is to allow ==
to work for thing<T,N>
and thing<R,N>
, that is, the N
's must be the same but the first template parameter can differ. I'm including this in case it affects a possible solution, since this is what I really need correct behavior for:
template <typename T, int N> struct thing {
operator const char * () const { return nullptr; }
template <typename R> bool operator == (const thing<R,N> &) const { return false; }
};
int main () {
thing<int,0> i0;
thing<float,0> f0;
thing<int,1> i1;
i0 == f0;
f0 == i0;
i0 == i1; // should fail to compile
f0 == i1; // should fail to compile
i1 == i0; // should fail to compile
i1 == f0; // should fail to compile
}
推荐答案
您可以在操作符不起作用的情况下提供操作符的 delete
d版本,例如:
You can just provide a delete
d version of the operator for the case where it shouldn't work, e.g.:
template <int N> struct thing {
operator const void * () const { return nullptr; }
bool operator == (const thing<N> &) const { return false; }
template <int X>
bool operator == (const thing<X> &) const = delete;
};
int main () {
thing<0> a;
thing<1> b;
a == a; // This compiles
a == b; // Doesn't compile
}
使用C ++ 20,该逻辑也方便地扩展到 operator!=
.使用C ++ 20之前的编译器,您可能还应该为 operator!=
添加相应的重载.
With C++20 the logic conveniently also extends to operator!=
. With pre-C++20 compiler you should probably add corresponding overloads for operator!=
, too.
这篇关于仅针对二进制运算符防止隐式转换运算符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!