【问题标题】:C++11 - templates, friends, decltype & access modifiersC++11 - 模板、朋友、decltype 和访问修饰符
【发布时间】:2011-12-22 10:42:01
【问题描述】:

你们怎么了,

我正在尝试为我的数学向量类重载加法运算符。 我的(看似逻辑正确)简化代码是:

template<typename T>
class Vector2
{
private:
    T       m_data[2];

    template<typename U>
    friend auto operator+(Vector2<T> a, Vector2<U> b) -> Vector2<decltype(a.m_data[0] + b.m_data[0])>
    {
        Vector2<decltype(a.m_data[0] + b.m_data[0])> ret(   a.m_data[0] + b.m_data[0],
                                                            a.m_data[1] + b.m_data[1]   );

        return ret;
    }

public:
    inline Vector2(T x, T y)
    {
        m_data[0] = x;
        m_data[1] = y;
    }
};

int main()
{
    Vector2<float>  v1(0.5f, 0.5f);
    Vector2<float>  v2(1, 2);

    v2 + v1; // Line 29

    return 0;
}

但是,GCC 4.6.1 给了我这个:

W:\projects\Awesome\BetterStuff\main.cpp||In function 'Vector2<decltype ((a.m_data[0] + b.m_data[0]))> operator+(Vector2<T>, Vector2<U>) [with U = float; T = float; decltype ((a.m_data[0] + b.m_data[0])) = float]':|
W:\projects\Awesome\BetterStuff\main.cpp|5|error: 'float Vector2<float>::m_data [2]' is private|
W:\projects\Awesome\BetterStuff\main.cpp|29|error: within this context|
||=== Build finished: 2 errors, 0 warnings (0 minutes, 0 seconds) ===|

如果我将第二个向量更改为 int 向量,则会出现更多(类似)错误。

我最接近解决这个问题的方法是找到这个有趣的页面:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48884

但遗憾的是,我不能用它来解决我自己的问题。 我尝试了 GCC 4.6.2 和 4.7.0,但我的代码也没有编译。

将“private”改为“public”确实解决了我的问题,但显然我的意图不是暴露m_data;

我只想定义一个加法运算符,它的返回类型由模板参数确定,据我了解,这是编译时的事情-对于模板函数的每个实例化,编译器都会自动计算出基于返回类型在 decltype() 那里。我的意思是,main() 以哪种方式尝试访问这些向量之一的 m_data 的内容?

这整件事让我很困惑,任何帮助将不胜感激。

好的,谢谢

【问题讨论】:

  • 如果有人感兴趣,因为我使用数字类型(具有 0 参数构造函数)作为 Vector2 的模板参数,我想我可以用这个替换旧的 decltype:decltype(T () + U())。虽然编译器为什么讨厌我的代码,但我仍然很感兴趣。
  • 有趣的是,如果 v1 和 v2 都是浮点数(即相同的类型),decltype(T() + U()) 工作。至少它适用于4.7。它仍然不适用于 float + int。

标签: c++ templates c++11 private decltype


【解决方案1】:

嗯,GCC 是对的……问题不在于 Vector2(float) 试图访问 Vector(int) 的私有成员,而在于 operator+(它只是Vector2(float)) 试图访问 Vector2(int) 的私有成员。所以更新后的代码是:

template<typename T>
class Vector2
{
    template<typename U>
    friend class Vector2;

private:
    T       m_data[2];

    template<typename T1, typename U>
    friend auto operator+(Vector2<T1> a, Vector2<U> b) -> Vector2<decltype(a.m_data[0] + b.m_data[0])>;

public:
    inline Vector2(T x, T y)
    {
        m_data[0] = x;
        m_data[1] = y;
    }

    inline Vector2<T>& operator=(const Vector2<T>& vec)
    {
        m_data[0] = vec.m_data[0];
        m_data[1] = vec.m_data[1];
    }
};

template<typename T, typename U>
auto operator+(Vector2<T> a, Vector2<U> b) -> Vector2<decltype(a.m_data[0] + b.m_data[0])>
{
    Vector2<decltype(T() + U())> ret(   a.m_data[0] + b.m_data[0],
                                        a.m_data[1] + b.m_data[1]   );

    return ret;
}

int main()
{
    Vector2<float>  v1(0.5f, 0.5f);
    Vector2<int>    v2(1, 2);

    //Vector2<int>  a = v2 + v1; // Doesn't work
    Vector2<float>  b = v2 + v1; // Works

    return 0;
}

【讨论】:

    【解决方案2】:

    如上所述,不要在 decltype 中使用私有成员。 另外,我公开了加法运算符。我不认为这是不合理的。 我还添加了坐标访问器。 这有效:

    template<typename T>
    class Vector2
    {
    private:
        T       m_data[2];
    public:
        template<typename U>
        friend auto operator+(Vector2<T> a, Vector2<U> b) -> Vector2<decltype(T() + U())>
        {
            Vector2<decltype(T() + U())> ret( a[0] + b[0],
                                              a[1] + b[1] );
    
            return ret;
        }
        inline Vector2(T x, T y)
        {
            m_data[0] = x;
            m_data[1] = y;
        }
        inline T operator[](int c) const { return m_data[c]; }
    };
    
    int main()
    {
        Vector2<float>  v1(0.5f, 0.5f);
        Vector2<int>  v2(1, 2);
    
        v2 + v1; // Line 29
    
        return 0;
    }
    

    假设 T 和 U 有默认的 ctors。

    【讨论】:

    • 如果您将'int' 用于 v2,因为我怀疑您打算这样做,您会再次遇到私人数据问题。我认为您需要(并且想要)一些公开的方式来获取个人坐标吗?
    • 再一次,如果我使用公共函数访问 m_data,有很多方法可以解决这个问题,但我试图避免它。也许 operator+ 不是一个很好的例子,因为我猜我的实际代码是“a += b; return b;”。但是,对于像 CrossProduct 这样的函数,我希望能够访问 m_data,即使它是私有的。
    【解决方案3】:

    简单地说:你不能访问另一个类的私有成员。 Vector2&lt;float&gt;Vector2&lt;int&gt;不同的类。您可以通过将其添加到您的课程来修复

    template<typename T> friend class Vector2;
    

    它将成为所有类型的Vector2 的朋友。然后你就可以访问另一个Vector2的隐私了。

    【讨论】:

    • 按照 K-ballo 的回答,我尝试将此行添加到 Vector2 类中,但不知何故绝对没有任何改变...同样的错误
    • @Matan:您应该发布最后一个编辑答案并将其标记为答案,因为我们其他人都错了。
    • 是的,我是新来的,只是想为您的帮助提供一些额外的声誉,但发布我的编辑作为答案确实有意义...谢谢
    【解决方案4】:

    问题在于Vector2&lt;T&gt;Vector2&lt;U&gt; 是完全不相关的不同类,如果T != U。您尝试做的与让class A 访问不相关的class B 的私有成员相同。

    为您的Vector2(可能是下标运算符)提供适当的访问器,并使用此类公共接口实现您的operators。

    【讨论】:

    • 我明白了。我非常喜欢能够访问向量类的私有成员——这也是我将操作员定义为朋友的原因。但是我添加了两个 Vector2,那么 Vector 怎么可能无法访问另一个 Vector 的私有成员?
    • 有趣的是他的代码和错误信息,TU 都是浮点数。我猜这是一个超级偏执的编译器?
    • 实际上,直到几天前我还在使用 MSVC++。由于 MSVC++ 中缺少 C++11 功能,我改用 GCC。据我所知,GCC 非常可靠,对吗? :)
    • @Matan:是的,GCC 的发布版本非常可靠。
    猜你喜欢
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 2014-10-07
    相关资源
    最近更新 更多