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

时间:2017-02-15 17:36:55

标签: c++ c++14 variable-templates

是否可以在内联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 个答案:

答案 0 :(得分: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);
}

plus centsToOctaves不再是C的成员函数,如评论中所述。

话虽如此,我不明白是什么阻止你这样做:

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

答案 1 :(得分: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);
}

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