【问题标题】:Should a friend operator in a class with enum parameters be found by Koenig lookup?Koenig 查找是否应该在具有枚举参数的类中找到友元运算符?
【发布时间】:2014-01-26 01:29:57
【问题描述】:

考虑以下代码:

enum A : unsigned { a = 1, b = 2, c = 4 };

class B
{
  friend constexpr A operator|(A a1, A a2)
  { return A(unsigned(a1) | unsigned(a2)); }
};

template <A a>
class C
{
};

int main()
{
  C<a|c> c;
}

使用 g++ (4.8.1, -std=c++1) 编译时,出现以下错误:

test.C: In function ‘int main()’:
test.C:16:12: error: invalid conversion from ‘unsigned int’ to ‘A’ [-fpermissive]
       C<a|c> c;

这告诉我找不到操作员。但是,如果代码改为:

enum A : unsigned { a = 1, b = 2, c = 4 };

constexpr A operator|(A a1, A a2)
{ return A(unsigned(a1) | unsigned(a2)); }

template <A a>
class C
{
};

int main()
{
  C<a|c> c;
}

然后它编译并运行良好。第一种情况下的错误是编译器错误,还是我误解了什么?我想使用 Boost.Operators 的方法来轻松定义运算符,方法是通过定义运算符的模板中的基类声明一个类,但在这种情况下,这个结果排除了这种情况。

感谢您的帮助。

【问题讨论】:

  • (pure) 依赖于参数的查找(又名 Koenig 查找)依赖于参数表达式,而不是调用的上下文。您是否希望 auto x = a|c; 找到在 B 中声明的运算符?
  • 你可能想解释一下你想为我们这些不熟悉 Boost.Operators 的人做些什么

标签: c++ c++11 operator-overloading


【解决方案1】:

解决它的另一种方法(除了已经提到的 jogojapan),就是在作用域之外简单地声明友元函数。

enum A : unsigned { a = 1, b = 2, c = 4 };

//declaration
constexpr A operator|(A a1, A a2);


class B
{
  //definition inside, ok.
  friend constexpr A operator|(A a1, A a2)
  { return A(unsigned(a1) | unsigned(a2)); }
};

template <A a>
class C
{
};

int main()
{
  C<a|c> c;
}

【讨论】:

  • 这个答案是正确的。问题不在于词法作用域,而在于类内定义的友元函数在类外是不可见的,除非/直到该函数的声明也在命名空间级别提供。 +1
  • 你是对的,但是这样一来,在类中定义这些函数并没有真正的好处。
  • 是的,我试图有类似的东西:template &lt;typename T&gt; class BitwiseEnum { friend constexpr T operator|(T, T) { ... }; friend constexpr T operator&amp;(T) { ...} friend constexpr T operator~(T) { ...} }; 然后可以用来为任何枚举“定义”这组运算符,而无需不断重写定义。 Enum A { ... }; class AWrapper : BitwiseEnum&lt;A&gt; {}; 之类的东西很遗憾,class AWrapper : BitwiseEnum&lt;AWrapper::A&gt; { Enum A { ... }; }; typedef AWrapper::A A; 也不起作用。
【解决方案2】:

好的,我采取了另一种方法,这是解决我问题的代码:

template <typename UNSIGNED>
struct BitwiseEnum
{
  enum class Enum : UNSIGNED
  {
  };

  constexpr static Enum value(unsigned bit)
  {
    return Enum(UNSIGNED(1) << bit);
  }

  friend constexpr Enum operator|(Enum a, Enum b)
  {
    return Enum(UNSIGNED(a)|UNSIGNED(b));
  }

  friend constexpr Enum operator&(Enum a, Enum b)
  {
    return Enum(UNSIGNED(a)&UNSIGNED(b));
  }

  friend constexpr Enum operator~(Enum a)
  {
    return Enum(~UNSIGNED(a));
  }
};

class MyMask : public BitwiseEnum<unsigned long>
{
};

typedef MyMask::Enum Mask;

constexpr static Mask a = MyMask::value(0);
constexpr static Mask b = MyMask::value(1);
constexpr static Mask c = MyMask::value(2);
constexpr static Mask d = MyMask::value(3);


template <Mask A>
class B
{
};

int main()
{
  B<a|b> test;
}

现在,当我定义了很多像“MyMask”这样的枚举时,它可以相当简单地完成,不需要比简单定义的枚举更多的代码。它的行为就像人们从位字段枚举中“期望”一样,可用于强类型检查的模板参数等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多