2013-03-01 146 views
2

考虑下面的代码:MSVC友元函数声明错误

#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; 
} 

正如你可以看到,这两个功能之间的唯一区别是第二个采用指向valueint!而非dummy<value>。 此代码编译在GCC就好($ GCC-4.7.2 TEST.CPP)和锵(感谢WhozCraig),但抛出MSVC以下错误(我测试2012):

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

对我来说,这看起来就像一个错误。但是,有没有人曾经遇到过这样的行为?这真的是一个错误,或者是错误的原因?任何快速解决方法?


编辑:我已经能够找到一个妥善的解决方法,请参阅answer below

+1

它可能不会帮助铿锵吃这个没问题。 – WhozCraig 2013-03-01 02:54:20

回答

2

这绝对是一个bug:A template function parametrized on the size of an array cannot be declared as a friend of a class。它发生在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; 
} 
+0

感谢您的链接!你的解决方案有效但是,使用它我没有显式指定(可能不正确)大小而丢失强大数组过载。更重要的是,当发生了一个'int *'超载时,你发布的第一段代码会产生歧义。我设法得到了一个更好的解决方法[这里](http://stackoverflow.com/a/15151021/823663)。 – a553 2013-03-01 05:16:03

0

很确定这是MSVS的一个已知问题。您的具体问题列于Portability Hints: Micrsoft Visual C++ on boost.org

寻找模板成为好友。我不知道周围的工作。不过,我认为你可以让一个班级成为朋友。您可以将其用作解决方法。

+0

对不起,但事实并非如此。你提到的文章是针对MSVC 6的;我正在使用MSVC 11.“作为朋友的模板”错误在很久以前就已经修复了,如果不是,我开篇文章中的第一个重载函数会发出一个错误。 – a553 2013-03-01 03:38:23

+0

你是对的,对不起。我没有。 – 2013-03-01 04:14:06

0

我已经想通,保留功能尚未确实防止错误信息的它的工作解决方法。这个想法是使用代理函数和代理类来传递指向数组的指针和它的大小。这里的解决方案:

#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; 
} 
+0

只是一个建议,但你可以避免使用'std :: vector'或'std :: array'([例如](http://liveworkspace.org/code/babIB$1)或[here]( http://liveworkspace.org/code/babIB$5))。 – 2013-03-01 08:32:54

+0

@JesseGood当然,但在我的情况下,泛型集合类型(适用于基于范围的C++ 11)已经由一个不同的重载处理,并且我需要普通的C数组。 – a553 2013-03-01 09:41:36

+0

我明白了,但是基于C++ 11范围的for可以处理纯C数组。 – 2013-03-01 09:48:28