【问题标题】:C++ Operator AmbiguityC++ 运算符歧义
【发布时间】:2010-09-27 21:43:48
【问题描述】:

请原谅我,因为我对 C++ 还很陌生,但是我在操作符歧义方面遇到了一些麻烦。对于在我的桌面上编译的代码,我认为它是特定于编译器的。但是,它无法在我的笔记本电脑上编译。我想我知道出了什么问题,但我没有看到一个优雅的解决方法。如果我犯了明显的错误,请告诉我。无论如何,这就是我想要做的:

我已经创建了自己的向量类 Vector4,它看起来像这样:

class Vector4
{
 private:
   GLfloat vector[4];
 ...
}

然后我有这些导致问题的运算符:

operator GLfloat* () { return vector; }

operator const GLfloat* () const { return vector; }

GLfloat& operator [] (const size_t i) { return vector[i]; }

const GLfloat& operator [] (const size_t i) const { return vector[i]; }

我有转换运算符,以便我可以将 Vector4 类的实例传递给 glVertex3fv,并且出于显而易见的原因我有下标。但是,涉及下标 Vector4 的调用对编译器来说会变得模棱两可:

enum {x, y, z, w}
Vector4 v(1.0, 2.0, 3.0, 4.0);

glTranslatef(v[x], v[y], v[z]);

以下是候选人:

candidate 1: const GLfloat& Vector4:: operator[](size_t) const
candidate 2: operator[](const GLfloat*, int) <built-in>

当已经在 Vector4 上定义了下标运算符时,为什么它会首先尝试将我的 Vector4 转换为 GLfloat*?有没有一种不涉及类型转换的简单方法?我只是犯了一个愚蠢的错误吗?感谢您提前提供任何帮助。

【问题讨论】:

  • 不需要应用于 size_t 参数的 const - 它毫无意义。但是,它是良性的,与您的问题无关。
  • 你应该告诉我们你正在使用什么编译器......
  • 我目前无法检查我在桌面上使用的编译器,但我在笔记本电脑上使用的编译器(这给了我错误)是 g++ 4.3.2。我正在使用 Eclipse,并默认打开这些标志:-O0 -g3 -Wall -c -fmessage-length=0

标签: c++ opengl operators ambiguity operator-keyword


【解决方案1】:

这在“C++ 模板 - 完整指南”一书中进行了解释。这是因为您的 operator[] 采用 size_t,但您传递了一个不同的类型,该类型首先必须经过隐式转换为 size_t。另一方面,也可以选择转换运算符,然后返回的指针可以是下标。所以有歧义。解决方案是删除转换运算符。如您所见,通常应该避免它们,因为它们只会引入问题。

提供一个beginend成员函数,分别返回vectorvector + 4。如果你想传递给原生的openGL函数,你可以使用v.begin()

cmets 有点混乱。我想我现在会更新这个答案以反映最新的概念。

struct Vector4 {
    // some of container requirements
    typedef GLfloat value_type;
    typedef GLfloat& reference;
    typedef GLfloat const& const_reference;

    typedef GLfloat * iterator;
    typedef GLfloat const * const_iterator;

    typedef std::ptrdiff_t difference_type;
    typedef std::size_t size_type;

    static const size_type static_size = 4;

    // returns iterators to the begin and end
    iterator begin() { return vector; }
    iterator end() { return vector + size(); }

    const_iterator begin() const { return vector; }
    const_iterator end() const { return vector + size(); }

    size_type size() const { return static_size; }
    size_type max_size() const { return static_size; }

    void swap(Vector4 & that) {
        std::swap(*this, that);
    }

    // some of sequences
    reference operator[](size_type t) { return vector[t]; }
    const_reference operator[](size_type t) const { return vector[t]; }

    // specific for us. returns a pointer to the begin of our buffer.
    // compatible with std::vector, std::array and std::string of c++1x
    value_type * data() { return vector; }
    value_type const* data() const { return vector; }

    // comparison stuff for containers
    friend bool operator==(Vector4 const&a, Vector4 const&b) {
        return std::equal(a.begin(), a.end(), b.begin());
    }
    friend bool operator!=(Vector4 const&a, Vector4 const&b) { return !(a == b); }
    friend bool operator<(Vector4 const&a, Vector4 const&b) {
        return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
    }
    friend bool operator> (Vector4 const&a, Vector4 const&b) { return b < a;    }
    friend bool operator<=(Vector4 const&a, Vector4 const&b) { return !(b < a); }
    friend bool operator>=(Vector4 const&a, Vector4 const&b) { return !(a < b); }

private:
    GLfloat vector[4];
}

【讨论】:

  • 非常感谢!我什至没有考虑 size_t 转换!我真的很喜欢你的解决方案。它非常符合我目前看到的 stl 约定。
  • 最好定义一个也只返回向量的 data() 函数。然后你用 data() 传递。原因是 begin() 返回一个迭代器,它被视为一个黑盒,它提供了迭代的工具,而 data() 返回一个指向实际数据的指针。 boost::array 遵循相同的设计
  • 噢!是的,我同意。 begin() 和 end() 应该返回一个迭代器,就像在 stl.我还没有遇到过data(),对boost的约定只知道一点。谢谢你的提示!我想我需要更多地熟悉这些约定,并阅读它们的论据。
  • 指针满足迭代器的所有要求,同时也是指针。我认为使用 begin() 返回指针没有任何问题。最初的方法会出现什么问题?
  • dribeas,当然指针是迭代器。我没有说别的。毕竟这就是您可以将 .begin() 提供给 STL 算法的原因。但重点是,迭代器并不总是指针。
【解决方案2】:

摆脱歧义太难了。它可以很容易地将其解释为直接 [] 访问,或强制转换为浮点*,然后是数组索引。

我的建议是放弃运算符 GLfloat*。让隐式强制转换以这种方式浮动只是在自找麻烦。如果您必须直接访问浮点数,请为 Vector4 创建一个 get()(或您选择的其他名称)方法,该方法返回指向下面的原始浮点数的指针。

其他随机建议:与其重新发明自己的矢量类,不如使用 OpenEXR 中的“IlmBase”包中的优秀矢量类

【讨论】:

  • 谢谢!我不知道转换运算符是个坏主意。我想因为我对 C++ 还很陌生,所以我还没有被他们烧死。我意识到肯定有很多很棒的向量和矩阵类,但我真的决定自己做更多的练习。
【解决方案3】:

为什么要向 operator[] 传递“const size_t”?

【讨论】:

  • 我在按值 size_t 参数之前使用 const 似乎存在问题。我知道这没有必要,但我认为它明确表明该值不应更改,因为我们将其用作索引。有人能解释一下为什么这么不受欢迎吗?
  • @Scott 并非不可取,但在许多情况下只是不必要的,因为复制 size_t 或 int 等固有类型是免费操作,通常它们都是按值传递的,因此不会在函数之外留下任何副作用- 这样的函数参数是函数的本地参数。 const size_t 有助于保持函数自描述并指示变量永远不会改变其值。
猜你喜欢
  • 2021-05-02
  • 1970-01-01
  • 2021-09-09
  • 2015-08-28
  • 2017-03-24
  • 1970-01-01
  • 2021-11-14
  • 2021-02-05
  • 1970-01-01
相关资源
最近更新 更多