仅针对二元运算符防止隐式转换运算符
Preventing implicit conversion operator only for binary operators
我遇到了一个问题,我已将其归结为以下问题,其中 ==
运算符使用编译即使它应该失败(C++17,在 GCC 上测试 5.x, 8.x, 和 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
}
它正在编译的原因是因为编译器选择这样做:
(const char *)a == (const char *)b;
而不是这个:
// bool thing<0>::operator == (const thing<0> &) const
a.operator == (b);
因为后者不匹配,因为 b
是 thing<1>
而不是 thing<0>
。 (顺便说一句,当使用原始比较运算符时,它还会产生 unused-comparison
警告;这并不有趣,但这就是出现这些警告的原因。)
我已经在 C++ Insights 上验证了这一点(实际上这是我发现原因的方式),输出:
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
的最后一行显示转换运算符的用法。
我的问题是:当然,如果我将转换运算符显式化,整个过程就会正常运行,但是 我真的很想保留隐式转换运算符 and 让编译器强制正确使用 operator ==
(其中“正确”= 如果参数类型不同则编译失败)。这可能吗?
请注意,让 thing<N> == const char *
工作对我来说 不 重要。也就是说,我不需要这个重载:
bool thing<N>::operator == (const char *) const
所以我不在乎解决方案是否打破了 ==
的那种味道。
我确实在这里搜索过其他帖子;有一些具有误导性的相似标题,但最终却毫无关联。看起来 有一个 相关的 问题(不需要的隐式转换),但它仍然不适用。
为了完整起见,这里有一个稍微不那么简单但更具代表性的例子,说明我实际在做什么,目的是让 ==
为 thing<T,N>
和 thing<R,N>
,即 N
必须相同,但第一个模板参数可以不同。我将其包括在内以防它影响可能的解决方案,因为这是我真正需要的正确行为:
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 版本以应对它不应该工作的情况,例如:
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!=
添加相应的重载。
如果您将 operator const char *
标记为 explicit
,那么您的代码将无法编译。
explicit
需要 C++11,但您已经在使用 nullptr
,所以这不是问题。
我遇到了一个问题,我已将其归结为以下问题,其中 ==
运算符使用编译即使它应该失败(C++17,在 GCC 上测试 5.x, 8.x, 和 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
}
它正在编译的原因是因为编译器选择这样做:
(const char *)a == (const char *)b;
而不是这个:
// bool thing<0>::operator == (const thing<0> &) const
a.operator == (b);
因为后者不匹配,因为 b
是 thing<1>
而不是 thing<0>
。 (顺便说一句,当使用原始比较运算符时,它还会产生 unused-comparison
警告;这并不有趣,但这就是出现这些警告的原因。)
我已经在 C++ Insights 上验证了这一点(实际上这是我发现原因的方式),输出:
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
的最后一行显示转换运算符的用法。
我的问题是:当然,如果我将转换运算符显式化,整个过程就会正常运行,但是 我真的很想保留隐式转换运算符 and 让编译器强制正确使用 operator ==
(其中“正确”= 如果参数类型不同则编译失败)。这可能吗?
请注意,让 thing<N> == const char *
工作对我来说 不 重要。也就是说,我不需要这个重载:
bool thing<N>::operator == (const char *) const
所以我不在乎解决方案是否打破了 ==
的那种味道。
我确实在这里搜索过其他帖子;有一些具有误导性的相似标题,但最终却毫无关联。看起来
为了完整起见,这里有一个稍微不那么简单但更具代表性的例子,说明我实际在做什么,目的是让 ==
为 thing<T,N>
和 thing<R,N>
,即 N
必须相同,但第一个模板参数可以不同。我将其包括在内以防它影响可能的解决方案,因为这是我真正需要的正确行为:
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 版本以应对它不应该工作的情况,例如:
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!=
添加相应的重载。
如果您将 operator const char *
标记为 explicit
,那么您的代码将无法编译。
explicit
需要 C++11,但您已经在使用 nullptr
,所以这不是问题。