嵌套在类中的枚举上的运算符重载



问题

给定以下代码:

template <typename T>
struct dummy {
  enum enumenum { a = 1, b = 2, c = 4 };
};
int main() { 
    // I want this line to expands as : 
    // dummy<double>::enumenum a = operator~(dummy<double>::a);
    auto a = ~dummy<double>::a;
}

如何在enumenum上重载运算符?我使用的是标准C++14。

我尝试了什么

一个天真的实现:

template <typename T>
typename dummy<T>::enumenum 
operator~(typename dummy<T>::enumenum a) {
  return static_cast<typename dummy<T>::enumenum>(operator~(a));
}

不幸的是,有问题的行扩展为:

int a = ~static_cast<int>(dummy<double>::a);

这意味着没有使用运算符(这是默认行为(。是因为ADL在结构名称空间中找不到正确的operator~()吗?

然后我尝试了:(注意friend(

template <typename T>
struct dummy {
  enum enumenum { a, b, c };
  
  friend enumenum operator~(enumenum a) { 
    return static_cast<enumenum>(~a);
  }
};

这实际上起作用并扩展为:

template <>
struct dummy<double> {
  enum enumenum {
    a = static_cast<unsigned int>(1),
    b = static_cast<unsigned int>(2),
    c = static_cast<unsigned int>(4)
  };
  friend inline dummy<double>::enumenum operator~(dummy<double>::enumenum a) {
    return static_cast<dummy<double>::enumenum>(operator~(a));
  }
};
int main()
{
  dummy<double>::enumenum a = operator~(dummy<double>::a);
}

这就是我想要的行为。但是,如果我不想在类主体中定义运算符,该怎么办。

所以我尝试了:

template <typename T>
struct dummy {
  enum enumenum { a = 1, b = 2, c = 4 };
  
  // if inline : inline function 'operator~' is not defined [-Wundefined-inline]
  // and adding inline to the template below does not help
  friend enumenum operator~(enumenum a);
};
template <typename T>
typename dummy<T>::enumenum 
operator~(typename dummy<T>::enumenum a) {
  return static_cast<typename dummy<T>::enumenum>(~a);
}
int main() { 
    auto a = ~dummy<double>::a; 
}

上面的代码扩展为:

template<>
struct dummy<double>
{
  enum enumenum
  {
    a = static_cast<unsigned int>(1), 
    b = static_cast<unsigned int>(2), 
    c = static_cast<unsigned int>(4)
  };
  
  friend dummy<double>::enumenum operator~(dummy<double>::enumenum a);
};
int main()
{
  dummy<double>::enumenum a = operator~(dummy<double>::a);
}

这会编译,但不会进行链接!编辑:我认为它没有链接,因为模板没有实例化,因此在链接时失败(类似于上面的天真实现(。

结论

尽管我以某种方式找到了实现我想要的东西的方法,但如果我不想在类定义中定义运算符,该怎么办。

提前谢谢。

这会编译,但不会链接!

编译但不链接,因为您声明了一个非模板运算符(它在模板结构中,但不是模板函数(

friend enumenum operator~(enumenum a);

然后定义一个模板一个

template <typename T>
typename dummy<T>::enumenum 
operator~(typename dummy<T>::enumenum a) {
  return static_cast<typename dummy<T>::enumenum>(~a);
}

并且模板定义不能与非模板声明匹配。

您可以尝试将函数声明为一个模板

template <typename T>
struct dummy
 {
   enum enumenum { a = 1, b = 2, c = 4 };
   template <typename U>
   friend typename dummy<U>::enumenum
      operator~ (typename dummy<U>::enumenum const & a);
 };
template <typename T>
typename dummy<T>::enumenum 
      operator~ (typename dummy<T>::enumenum const & a)
 { return static_cast<typename dummy<T>::enumenum>(~a); }
int main ()
 { 
   auto a = ~dummy<double>::a; 
 }

但是,不幸的是,这个编译但是,调用~运算符如下

 ~dummy<double>::a;

没有被称为模板函数,因为模板参数T无法推导,因为它处于未推导的上下文中(在最后一个::之前(,正如Benny K在评论中指出的那样。

因此,代替模板operator~(),将dummy<double>::a转换为int,并应用int~运算符(具有类型结果int(。

您可以显式调用函数operator~() 来验证这一点

 auto a = operator~(dummy<double>::a); 

你应该得到一个";对"operator~"的调用没有匹配函数;错误(带有注释"注释:忽略候选模板:无法推断模板参数"t"(或类似内容。

为了使这个解决方案发挥作用,你必须解释类的类型,以避免模板推导

 auto a = operator~<double>(dummy<double>::a); 

现在,您可以验证adummy<double>::enumenum

 static_assert( std::is_same<decltype(a), dummy<double>::enumenum>::value, "!" );

但是,很明显,这不是一个令人满意的解决方案(如果您忘记了避免简单使用~,则非常危险(。

否则,您可以将操作员定义为非模板

template <typename T>
struct dummy
 {
   enum enumenum { a = 1, b = 2, c = 4 };
   friend enumenum operator~ (enumenum const & a);
 };
dummy<double>::enumenum 
      operator~(dummy<double>::enumenum const & a)
 { return static_cast<dummy<double>::enumenum>(~a); }
int main ()
 { 
   auto a = ~dummy<double>::a; 
 }

但是必须为每个CCD_ 17类型定义不同的运算符。

IMHO最简单、安全和优雅的解决方案是您的工作解决方案:在结构中声明/定义运算符。

最新更新