在不公开变量模板的情况下在内联 constexpr 函数中使用变量模板?

Using a variable template inside inline constexpr function without exposing the variable template?

是否可以在内联 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 没有)。

为了更详细地重复动机,该函数是内联的并在 header 中定义,我对在包含 header 的任何地方公开变量模板不感兴趣。

我想我可以定义一个详细命名空间并将变量模板放在那里,但最好不要公开变量模板。也许这不可能。

根据我们的标准:

A template-declaration is a declaration. [...]. A declaration introduced by a template declaration of a variable is a variable template. [...]

并且:

A template-declaration can appear only as a namespace scope or class scope declaration.

因此不,这是不允许的。
如果您不想公开它,您仍然可以将其包装在 class 中并将数据成员和成员函数设为静态:

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,并将其声明为私有。 不允许在函数范围内声明模板。

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);
}

但遗憾的是,此解决方案会生成一些重复代码,可能会更糟。