仅针对二元运算符防止隐式转换运算符

我有一个问题,我归结为以下问题,==即使操作符使用编译失败(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);

因为后者不是匹配项,因为bis a thing<1>not a 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.

我的问题是:当然,如果我使转换运算符显式,整个事情就会正常运行,但我真的很想保留隐式转换运算符让编译器强制正确使用operator ==(where "correct" = failed to compile if参数类型不同)。这可能吗?

请注意,有工作对我来说并不重要thing<N> == const char *。也就是说,我不需要这个重载:

bool thing<N>::operator == (const char *) const

所以我不在乎解决方案是否打破了==.

我确实在这里搜索了其他帖子;有一些具有误导性的相似标题,但最终却没有关联。看起来这篇文章有一个相关的问题(不需要的隐式转换),但它仍然不适用。


为了完整起见,这里有一个稍微不那么简单但更具代表性的例子,说明我实际在做什么,其目的是允许==thing<T,N>and工作thing<R,N>,也就是说,N's 必须相同,但第一个模板参数可以不同。我将其包括在内,以防它影响可能的解决方案,因为这是我真正需要的正确行为:

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!=


以上是仅针对二元运算符防止隐式转换运算符的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>