在内联constexpr函数内使用可变模板而不暴露变量模板?

问题描述:

是否可以在内联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++并不是真正的那种语言,你无论如何都不可能做出不好的行为。 –

从标准,我们有:

模板声明是一个声明。 [...]。由变量的模板声明引入的声明是可变模板。 [...]

和:

模板声明只能出现一个命名空间范围或类范围的声明。

因此不,不允许。
你仍然可以把它包装在一个类中,使两者的数据成员和成员函数的静态,如果你不希望暴露它:

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

正如我在其他答案上写的,不应该认为可以将一个自由函数作为一个类的静态函数来代替,这具有相当广泛的含义。 –

除了使用了命名空间,你也可以把模板变量为一个类,并将其声明为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