2015-10-15 47 views
24

我有下面的代碼在g ++下編譯,但不是與鏗鏘編譯。這是有效的C++ 11

如果以各種次要方式更改代碼(如合併2個名稱空間聲明),Clang將編譯代碼。

// The problem disappears without namespaces. 
namespace Root { 
    // The problem disappears if 'g' is in the global namespace, and we change 
    // the friend declaration to '::g' 

    // The problem disappears if 'g' has void return type. 

    // The problem disappears if we get rid of the 'Value' template argument 
    // and the 'value' parameter. 
    template<typename Value, typename Defaulted = void> 
    bool g(Value value); 

    // The problem disappears if MyClass is not a template. 
    template<typename ClassValue> 
    class MyClass { 
    private: 
     template<typename Value, typename Defaulted> 
     friend bool g(Value value); 
    }; 
} 

// The problem disappears if we declare the Root namespace in a single block 
// containing 'g', 'MyClass' and 'f'. 

// The problem remains if we declare f in the global namespace and reference 
// Root::g. 
namespace Root { 
    void f() { 
     MyClass<int> value; 

     g(value); 
    } 
} 

要使用鐺編譯:

clang -fsyntax-only -std=c++11 testcase.cpp 

要使用克++編譯:

g++ -fsyntax-only -std=c++11 testcase.cpp 

版本克++ 4.9.2,3.6.0鐺,既在Ubuntu芯15.04。

鏘給人的錯誤消息:

testcase.cpp:24:9: error: no matching function for call to 'g' 
     g(value); 
     ^
testcase.cpp:14:21: note: candidate template ignored: couldn't infer template argument 'Defaulted' 
     friend bool g(Value value); 
       ^
1 error generated. 
+0

友元函數(即使在一個類中聲明)的命名空間範圍。所以在這種情況下,你有兩個'bool Root :: g()'函數聲明。模板參數不會更改聲明。這就是爲什麼你的小改動使這個代碼有效。我實際上更驚訝,這與g ++編譯。 –

+3

@SimonKraemer您可以多次聲明函數。無論如何,「朋友」聲明不會重新聲明函數。 – Barry

+0

這是您的完整代碼或只是相關的部分? –

回答

7

我相信這是一個鏗鏘錯誤。從[temp.param],我們有:

如果朋友函數模板聲明指定 默認模板參數,該聲明應定義,並應在 函數模板的唯一聲明翻譯單位。

該組默認模板參數可供使用通過從 合併默認參數的模板的所有先前聲明的相同方式函數默認參數獲得的是(8.3.6)。

後一點意味着我們可以寫出:

template <typename T, typename U=int> 
void h(); 

template <typename T, typename U> 
void h() { } 

h<int>(); 

,這是非常清楚形成鐺編譯代碼。我們無法指定默認的模板參數根據公佈的規則g,爲g先前聲明,但不指定它應該仍然保持可通過歸併步驟作爲voidDefaulted。如果默認參數可用,則查找應該能夠找到我們想要的g

一種解決方法是簡單地的朋友,我們關心的專業化:

friend bool g<>(MyClass value); 
+0

倒數到@ T.C。糾正我在5 ... 4 ... – Barry

+0

謝謝 - 我同意你對合並的看法,這對我有意義。但我認爲你不允許指定一個模板參數默認在一個朋友decl中。因此,我對第一段的解釋是:通過聲明一個已經有默認值(在別處聲明)的模板參數的朋友,我必須在朋友聲明旁邊提供函數體? –

+0

經過更多思考,我現在明白了。你可以在朋友模板上指定默認值,但我沒有意識到這一點,這讓我感到困惑。所以這看起來非常像一個叮噹聲bug。 –