2016-04-04 34 views
13

我一直沒有找到更好的標題,但如果您有正確的想法,可隨時修改它。實際上,它總比GCC vs clang反正。變量模板和函數的指針:什麼編譯器是正確的?


我試圖找出什麼是錯在此代碼:

template <typename... T> 
struct S; 

template <typename T, typename... U> 
struct S<T, U...>: S<U...> { 
    using S<U...>::f; 

    template<void(*F)(const T &)> 
    void f() { } 
}; 

template<> 
struct S<> { 
    void f(); 
}; 

template<typename... T> 
struct R: S<T...> { 
    using S<T...>::f; 

    template<typename U, void(*F)(const U &)> 
    void g() { 
     this->template f<F>(); 
    } 
}; 

void h(const double &) { } 

int main() { 
    R<int, double> r; 
    r.g<double, h>(); 
} 

它編譯與GCC 4.9(見here),但它不與鐺3.8編譯.0(見here)。

哪個編譯器是正確的,爲什麼?
此外,我能做些什麼來查看編譯器編譯的代碼?

+0

這很奇怪(還在測試),現在我可以建議在問題中添加標誌「variadic-templates」。 – Radek

+0

看來ICC也無法編譯這個:https://godbolt.org/g/N67e2z – NathanOliver

+0

也許與[此問題]相關(http://stackoverflow.com/questions/8629839/c-candidate-template-ignored -invalid-explicit-specified-argument-for-templ)或[this question](http://stackoverflow.com/questions/33872039/invalid-explicitly-specified-argument-in-clang-but-successful-compilation-in -GCC)? – callyalater

回答

9

我相信clang在這裏是正確的,這是一個海灣合作委員會的錯誤。首先,讓我們開始一個簡化的例子:

struct U { 
    template<int > void foo() { } 
}; 

struct X : U { 
    using U::foo; 
    template<void* > void foo() { } 
}; 

int main() { 
    X{}.foo<1>(); // gcc ok, clang error 
} 

[namespace.udecl]

using聲明帶來從基類聲明成派生類,成員函數和 成員函數模板在派生類中覆蓋和/或隱藏 基類中的相同名稱,參數類型列表(8.3.5),cv-qualification和ref限定符(如果有)的模板成員函數和成員函數 而不是相互衝突)。 使用聲明引入的一組 聲明中排除了此類隱藏或重寫聲明。

U::fooX::foo具有相同的名稱,參數類型列表(無&匕首;),CV-資格(無),和REF限定符(無)。因此,X::foo隱藏U::foo而不是重載它,我們肯定是通過錯誤類型X::foo

只需用不同的函數指針類型的重載(而不是把他們作爲模板參數)正常工作:

template <typename T, typename... U> 
struct S<T, U...> : S<U...> { 
    using S<U...>::f; 

    void f(void (*F)(const T&)) { } 
}; 

或者,如果你真的需要的函數指針作爲模板參數,仍可超載包裝他們在標籤類型:

template <class T> 
using fptr = void(*)(const T&); 

template <class T, fptr<T> F> 
using func_constant = std::integral_constant<fptr<T>, F>; 

和傳播通過:

template <typename T, typename... U> 
struct S<T, U...>: S<U...> { 
    using S<U...>::f; 

    template <fptr<T> F> 
    void f(func_constant<T, F>) { } 
}; 

和:

template <class U, fptr<U> F> 
void g() { 
    this->f(func_constant<U, F>{}); 
} 

&dagger;parameter-type-list的定義沒有提及模板參數。

+0

但參數類型列表確實提到了來自模板聲明的參數包。所以,這並不意味着模板會被認爲是超載與隱藏的函數解析? – callyalater

+0

@callyalater但是參數包仍然是一個參數。模板參數不是。 – Barry

+0

@Barry一種標籤調度,對吧?我也這麼認爲,但我想知道這是否是最合適的解決方案。謝謝你的詳細回覆(像往常一樣,非常感謝)。 – skypjack

3

@barry後 - 改變f的定義S專業化:

template <typename T, typename... U> 
struct S<T, U...>: S<U...> { 
    using S<U...>::f; 


    template<void(*F)(const T &)> 
    void f(T *= nullptr) { } 
}; 

使得還鐺與您的代碼工作。

編輯:

爲了使代碼更簡單,你可以改變專門化:

template <typename T, typename... U> 
struct S<T, U...>: S<T>, S<U...> { 
}; 

template<typename T> 
struct S<T> { 
    template<void(*F)(const T &)> 
    void f() { } 
} 

,然後使用調用你的f方法g

S<U>::template f<F>(); 

使用這個選項,你可以更進一步,因爲@ barry建議使用標籤調度,但我n模板參數而不是作爲函數的參數:

template <typename... T> 
struct S; 

template <typename T> 
struct footag { }; 

template <typename T, typename... U> 
struct S<T, U...>: S<footag<T>>, S<U...> { 
}; 

template<typename T> 
struct S<T>:S<footag<T>> { 
}; 

template<typename T> 
struct S<footag<T>> { 
    template <void (*F)(const T&)> 
    void f() { } 
}; 

template<typename V, typename... T> 
struct R: S<V, T...> { 
    template<typename U, void(*F)(const U &)> 
    void g() { 
     S<footag<U>>::template f<F>(); 
    } 
}; 

void h(const float &) { } 

int main() { 
    R<int, float, double> r; 
    r.g<float, h>(); 
} 
+1

那麼'nullptr'而不是'NULL'會更多* C++ 11 * -ish,不是嗎? – skypjack

+0

當然,它會... :)編輯 –

+0

我想依靠標籤調度是一個更好的解決方案。你提出的看起來更像是一種基於混合的方法,但這不是我要找的(不要誤解,我真的很喜歡mixin,但這不是我用它們的問題)。是否有意義? – skypjack