如何在不与标准库运算符冲突的情况下为一组相关类的模板重载运算符? [英] How do I template overload an operator for a group of related classes without conflicting with standard library operators?

查看:70
本文介绍了如何在不与标准库运算符冲突的情况下为一组相关类的模板重载运算符?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这似乎是一个相当简单的想法:我有一组类,我应该能够为这些类写一个运算符,比如用基本相同的代码进行减法.

This seems like a fairly straight forward idea: I have a group of classes for which I should be able to write an operator, let's say subtraction, using basically the exact same code.

当尝试以显而易见的"方式执行此操作时,即:

When trying to do this the "obvious" way, i.e:

template <typename T>
T operator-(T a, const T& b) {
    return a -= b;
}

然后,在某些编译器上,这似乎与迭代器的减法运算符发生冲突(特别是在gcc 3.4.6和Apple LLVM上中断,而在gcc版本4或更高版本上似乎可以正常工作),并且出现以下错误:

Then, on certain compilers, this seems to conflict with a subtraction operator for iterators (specifically it breaks on gcc 3.4.6 and Apple LLVM, while seemingly working fine on gcc version 4 or newer), and I get the following error:

 error: use of overloaded operator '-' is ambiguous (with operand types 'std::__1::__wrap_iter<const double *>' and 'const_iterator'
      (aka '__wrap_iter<const_pointer>')) 

我还考虑了仅针对基类的模板重载,该基类将从中派生组中的所有类,但是由于第一个参数是按值传递的,因此我认为特定于该子类的信息将在重载期间丢失.复制.

I also considered template overloading for just a base class from which all of the classes in the group would be derived from, but then since the first parameter is passed by value, I think information specific to the subclass would be lost during the copy.

我缺少明显的东西吗?有没有一种方法可以使所有这些编译器保持满意?

Am I missing something obvious? Is there a way to do this that keeps all these compilers happy?

理想情况下,该解决方案应在C ++ 03中工作.

the solution should ideally work in C++03.

推荐答案

使用基类,adl koenig运算符和sfinae.

Use a base class, adl koenig operators, and sfinae.

namespace ops{
  struct subtract_support{
    template<class T,
      std::enable_if_t<std::is_base_of<subtract_support, T>{}, int> =0
    >
    friend T operator-( T lhs, T const& rhs ){
      lhs-=rhs;
      return lhs;
    }
  };
}

现在从ops::subtract_support继承会导致-适用于您的类型. (请注意两行的正文:确保lhs从-中移出,与OP中的版本不同).

Now inheriting from ops::subtract_support causes - to work for your type. (Note the two-line body: that ensures the lhs is moved out of -, unlike your version in the OP).

仅对名称空间的限制会导致您发现-的任何模板生成类型,其中tge参数之一来自您的名称空间,以及其他意外情况:adl对其模板成员来自特定名称空间的类型进行操作. 1

Merely namespace restricting results in your - being found any template produced type where one of tge arguments comes from your namespace, and other unintended cases: adl operates on types whose template members come from a specific namespace.1

此技巧可让您在声明时将每种类型标记为使用此技术.

This trick lets you mark each type as using this technique at the point of declaration.

此技术几乎所有二进制布局都为零.但是,如果您需要诸如前缀的布局兼容性之类的晦涩之处,则可能需要使用traits类.

There are almost zero binary layout implications of this technique. But if you need something obscure like layout compatibility of prefixes a traits class may be required instead.

现在,这是C ++ 11解决方案.显然,OP需要C ++ 03.好吧,最好的前进方法是像C ++ 11一样实现它,以便在升级编译器时可以删除代码.

Now, this is a C++11 solution. Apparently the OP needs C++03. Well, the best way forward is to implement it as much like C++11 so code can be deleted when you upgrade your compiler.

enable_if可以轻松地用C ++ 03编写. is_base_of需要多几行:

enable_if can easily be written in C++03. is_base_of takes a few more lines:

namespace notstd{
  namespace details{
    template<class T, class U>
    struct base_test{
      typedef char no; // sizeof(1)
      struct yes { no unused[2]; }; // sizeof(2) or greater
      static yes test(T*); // overload if arg is convertible-to-T*
      static no test(...); // only picked if first overload fails
      // pass a `U*` to `test`.  If the result is `yes`, T
      // is a base of U.  Note that inaccessible bases might fail here(?)
      // but we are notstd, good enough.
      enum {value= (
        sizeof(yes)==sizeof(test((U*)0))
      )};
    };
  }
  template<class Base, class T>
  struct is_base_of{
    enum{value=details::base_test<Base,T>::value};
  };
  template<bool b, class T=void>
  struct enable_if {};
  template<class T>
  struct enable_if<true, T> {
    typedef T type;
  };
}

我们还需要在模板ADL运算符中调整SFINAE,以符合C ++ 03:

We also need to tweak the SFINAE in the template ADL operator for C++03 compliance:

namespace ops{
  struct subtract_support{
    template<class T>
    friend
    typename notstd::enable_if<notstd::is_base_of<subtract_support, T>::value, T>::type
    operator-( T lhs, T const& rhs ){
      lhs-=rhs;
      return lhs;
    }
  };
}

实时示例.

1 例如,如果Foo是命名空间中带有贪婪的-模板运算符的类型,则decltype(v0-v1)其中v0v1vector<Foo>将为vector<Foo>.那是一个误报(它将不会编译).但是具有自己的-vec3<Foo>(3 Foo的向量空间)将导致相同的歧义.

1 As an example, if Foo is a type in the namespace with a greedy - template operator, then decltype(v0-v1) where v0 and v1 are vector<Foo> will be vector<Foo>. That is a false positive (it will not compile). But a vec3<Foo> (vector space of 3 Foo) with its own - would result in the same ambiguity.

这篇关于如何在不与标准库运算符冲突的情况下为一组相关类的模板重载运算符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆