MSVC友元函数声明错误

MSVC friend function declaration bug

本文关键字:错误 声明 函数 友元 MSVC      更新时间:2023-10-16

考虑以下代码:

#include <cstddef>
template<size_t value> class dummy { };
class my_class
{
    int m_member;
    // Overload 1
    template<size_t value>
    friend void friend_func(dummy<value>*);
    // Overload 2
    template<size_t value>
    friend void friend_func(int(*)[value]);
};
// Overload 1
template<size_t value>
void friend_func(dummy<value>*)
{
    my_class instance;
    instance.m_member = value;
}
// Overload 2
template<size_t value>
void friend_func(int(*)[value])
{
    my_class instance;
    instance.m_member = value;
}
int main(int argc, char* argv[])
{
    dummy<5> d;
    friend_func(&d);    // call Overload 1
    int arr[5];
    friend_func(&arr);  // call Overload 2 - error in MSVC!
    return 0;
}

可以看到,这两个函数之间的唯一区别是第二个函数使用指向value int s而不是dummy<value>的指针。这段代码在GCC ($ GCC -4.7.2 test.cpp)和Clang(感谢WhozCraig)中编译得很好,但在MSVC(我测试了2012)中抛出以下错误:

1>d:pathto.cpp(32): error C2248: 'my_class::m_member' : cannot access private member declared in class 'my_class'
1>          d:pathto.cpp(8) : see declaration of 'my_class::m_member'
1>          d:pathto.cpp(7) : see declaration of 'my_class'
1>          d:pathto.cpp(40) : see reference to function template instantiation 'void friend_func<5>(int (*)[5])' being compiled

对我来说,这看起来像一个bug。但是,以前有人遇到过这样的行为吗?这真的是一个bug吗,或者可能有一个特定的错误原因?有什么快速的解决方法吗?


编辑:我已经能够找到一个适当的解决方案,见下面的答案。

这绝对是一个错误:不能将数组大小参数化的模板函数声明为类的友元。当value被推断为友模板函数的数组大小时,就会发生这种情况。以下是代码的简化版本,可以很好地编译。这个例子是完全相同的代码,除了我指定了数组的大小。

class my_class
{
    int m_member;
    template<size_t value>
    friend void friend_func(int(*)[5]);
};
template<size_t value>
void friend_func(int(*)[5])
{
    my_class instance;
    instance.m_member = value;
}
int main()
{
    int arr[5];
    friend_func<5>(&arr);
}
一种解决方法是将value作为第二个函数参数传递:
template <typename T>
void friend_func(T, int value)
{
    my_class instance;
    instance.m_member = value;
}

非常确定这是MSVS的已知问题。您的具体问题在boost.org上的可移植性提示:microsoft Visual c++中列出。

寻找模板作为朋友。我不知道周围的工作。然而,我认为你可以让一个班级成为朋友。你可以用它来解决这个问题。

我已经找到了一个解决方法,既保留了功能,又能防止错误消息的出现。其思想是使用代理函数和代理类来携带指向数组及其大小的指针。下面是解决方案:

#include <cstddef>
// Workaround class for a bug in MSVC.
// https://connect.microsoft.com/VisualStudio/feedback/details/717749
// http://stackoverflow.com/questions/15149607
template<class element_type, size_t count>
class friend_declaration_bug_workaround
{
public:
    typedef element_type(*ptr_type)[count];
private:
    ptr_type m_arr;
public:
    explicit friend_declaration_bug_workaround(ptr_type arr)
        : m_arr(arr)
    {
    }
    ptr_type value() const
    {
        return m_arr;
    }
};
class my_class
{
    int m_member;
    friend void friend_func(int*);
    template<size_t value>
    friend void friend_func_workaround(friend_declaration_bug_workaround<int, value>);
};
template<size_t value>
void friend_func_workaround(friend_declaration_bug_workaround<int, value> workaround)
{
    my_class instance;
    instance.m_member = (*workaround.value())[0];
}
void friend_func(int* arr)
{
    my_class instance;
    instance.m_member = *arr;
}
template<size_t value>
void friend_func(int(*arr)[value])
{
    friend_declaration_bug_workaround<int, value> workaround(arr);
    return friend_func_workaround(workaround);
}
int main(int argc, char* argv[])
{
    int value;
    friend_func(&value);    // call non-templated function
    int arr[5];
    friend_func(&arr);      // call workarounded function
    return 0;
}