2013-03-01 140 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