Использование шаблона переменной внутри встроенной функции 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 выдает информативное сообщение об ошибке, Clang - нет).
Чтобы повторить мотивацию более подробно, функция встроена и определена в заголовке, и я не заинтересован в представлении шаблона переменной везде, где включен заголовок.
Я предполагаю, что могу определить детальное пространство имен и поместить туда шаблон переменной, но было бы лучше вообще не раскрывать шаблон переменной. Может быть, это невозможно.
2 ответа
Из стандарта получаем, что:
Шаблон-объявление - это объявление. [...]. Объявление, введенное посредством объявления шаблона переменной, является шаблоном переменной. [...]
А также:
Объявление шаблона может появляться только как область видимости пространства имен или декларация области класса.
Поэтому нет, это не разрешено.
Вы все еще можете обернуть его в класс и сделать статический элемент-член и функцию-член статическим, если вы не хотите предоставлять его:
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};
}
Помимо использования пространства имен, вы также можете поместить переменную шаблона в класс и объявить ее как приватную. Объявление шаблона в области действия не допускается.
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;
}
Unrelated:
Возможно, вам не нужно объявлять шаблон 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);
}
Но, к сожалению, это решение сгенерирует некоторый дублированный код, может быть и хуже.