4

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

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

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

4

2 回答 2

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

如评论中所述,它具有不再是 的成员函数的加号centsToOctavesC

话虽这么说,我不明白是什么阻止你简单地这样做:

template<typename T>
inline constexpr T centsToOctaves(const T cents) {
    return cents / T{1200};
}
于 2017-02-15T17:45:05.483 回答
0

除了使用命名空间之外,您还可以将模板变量放入一个类中,并将其声明为私有。不允许在函数范围内声明模板。

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

但遗憾的是,这个解决方案会产生一些重复的代码,可能是更糟糕的一个。

于 2017-02-15T17:46:23.013 回答