2017-02-15 82 views
4

是否可以在內聯constexpr函數內使用變量模板而不暴露變量模板本身?在內聯constexpr函數內使用可變模板而不暴露變量模板?

例如,該編譯和工作原理:

template<typename T> constexpr T twelve_hundred = T(1200.0); 

template<typename T> 
inline constexpr T centsToOctaves(const T cents) { 
    return cents/twelve_hundred<T>; 
} 

但是,這並不編譯:

template<typename T> 
inline constexpr T centsToOctaves(const T cents) { 
    template<typename U> constexpr U twelve_hundred = U(1200.0); 
    return cents/twelve_hundred<T>; 
} 

的原因似乎是,模板聲明在塊範圍內不準(GCC給出了關於這方面的信息錯誤消息,鐺不)。

要更詳細地重複動機,該函數是內聯的並且在頭文件中定義,並且我不想在包含頭文件的任何位置公開變量模板。

我想我可以定義一個詳細的命名空間,並把變量模板放在那裏,但更好的是不暴露變量模板。也許這是不可能的。

+0

認爲你可以做的最好的事情就是讓它成爲一個私人(靜態)成員,一個朋友你的免費功能。也就是說,我不認爲重複和樣板文件只是把它放在一個詳細的命名空間中是值得的。這是一個非常老套的習慣,而C++並不是真正的那種語言,你無論如何都不可能做出不好的行爲。 –

回答

3

從標準,我們有:

模板聲明是一個聲明。 [...]。由變量的模板聲明引入的聲明是可變模板。 [...]

和:

模板聲明只能出現一個命名空間範圍或類範圍的聲明。

因此不,不允許。
你仍然可以把它包裝在一個類中,使兩者的數據成員和成員函數的靜態,如果你不希望暴露它:

class C { 
    template<typename T> 
    static constexpr T twelve_hundred = T(1200.0); 

public: 
    template<typename T> 
    static constexpr T centsToOctaves(const T cents) { 
     return cents/twelve_hundred<T>; 
    } 
}; 

int main() { 
    C::centsToOctaves(42); 
} 

另一種可能的解決方案是:

class C { 
    template<typename T> 
    static constexpr T twelve_hundred = T(1200.0); 

    template<typename T> 
    friend inline constexpr T centsToOctaves(const T cents); 
}; 

template<typename T> 
inline constexpr T centsToOctaves(const T cents) { 
    return cents/C::twelve_hundred<T>; 
} 

int main() { 
    centsToOctaves(42); 
} 

它有centsToOctaves不再是C的成員函數,如註釋中所述。

話雖這麼說,我不明白是什麼阻止你只是這樣做:

template<typename T> 
inline constexpr T centsToOctaves(const T cents) { 
    return cents/T{1200}; 
} 
+0

是的,我在上面寫過。問題是如果有一種方法不暴露變量模板。 – Danra

+0

@達拉剛剛更新了答案。 – skypjack

+0

正如我在其他答案上寫的,不應該認爲可以將一個自由函數作爲一個類的靜態函數來代替,這具有相當廣泛的含義。 –

0

除了使用了命名空間,你也可以把模板變量爲一個類,並將其聲明爲private 。 不允許在函數作用域中聲明模板。

class Detail { 
public: 
    template<typename T> 
    static constexpr T centsToOctaves(const T cents) { 
    return cents/twelve_hundred<T>; 
    } 

private: 
    template<typename U> 
    static constexpr U twelve_hundred = U(1200.0); 
}; 

// forwarding 
template<typename T> 
inline constexpr T centsToOctaves(const T cents) { 
    return Detail::centsToOctaves<T>(cents); 
} 

int main() { 
    centsToOctaves<int>(12); 
    return 0; 
} 

無關:

你可能不需要申報模板constexpr變量。既然你不能初始化後修改,替代實現,可直接使用文字:

template<typename T> 
inline constexpr T centsToOctaves(const T cents) { 
    using U = T; 
    return cents/U(1200.0); 
} 

而當你需要明確專門的模板變量,你可以專門功能模板。

template <> 
inline constexpr int centsToOctaves(const int cents) { 
    using U = int; 
    return cents/U(1200.0); 
} 

但不幸的是,這種解決方案會產生一些重複的代碼,可能會更糟。

+0

對類靜態函數的內聯是隱含的和冗餘的。此外,不知道爲什麼你會轉發,而不是隻宣佈一個朋友? –

+0

事實上,這是一個解決方案,但不幸的是,在將函數嵌入到類中的情況下(如我的情況),這不是一個好的解決方案。 – Danra

+0

@NirFriedman我首先聲明瞭成員函數,然後我意識到'centsToOctaves'是一個非成員函數,因此,在那一刻,我認爲使用成員函數訪問私有成員變量會更好。然後再想一想......你知道發生了什麼。 – felix