【问题标题】:Is there a way to use variable template as a parameter?有没有办法使用变量模板作为参数?
【发布时间】:2020-11-25 21:11:13
【问题描述】:

我想声明一个函数,它将一个变量(比如说,int)作为参数,该变量应该由一个类参数化。说到 lambda 演算,我希望我的参数有一种 * -> int

我希望能够编写的函数示例(Spec 是变量):

template <??? Specification, typename T>
auto make_array() {
    return std::array<T, Specification<T>>;
}

由于 C++14 我们有可变模板,所以我们可以这样做:

template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;

问题是,我如何将它传递给函数?在cppreference 的注释部分指出

变量模板不能用作模板模板参数。

但这是否意味着实际上没有办法将参数化变量作为函数参数传递?例如,您可以创建一个具有表示值的静态字段的类,但一个明显的缺点是我的函数的用户必须从该类派生。

我相信使用 SFINAE 可能会有一些解决方法,但我缺乏这方面的技能。

【问题讨论】:

  • 措辞有点混乱,因为你在谈论一个函数参数,但代码看起来你想要一个模板参数,而且引用是关于模板参数的
  • @idclev463035818 我很好奇实现这样一个功能的任何方式,但我猜它可以被认为是一个模板参数。
  • @Sprite 除了提供的答案,它确实回答了我的问题。
  • 您的示例使用int,因为您想将“specification&lt;T&gt; 的值”作为模板参数传递给std::array,对吗?注意std::array的参数是size_t不是int(我第一次在我的回答中犯了同样的错误)

标签: c++ templates c++14 sfinae


【解决方案1】:

除非你坚持使用变量模板,否则你可以使用类型特征:

template <typename T> struct Specification;

您可以将其专门用于int

template <>
struct Specification<int> {
    static constexpr size_t value = 42;
};

如果您想拥有不同的Specifications,请将其作为模板模板参数传递:

template <template<class> class S, typename T>
auto make_array() {
    return std::array<T, S<T>::value>{};
}

Complete example:

#include <array>
#include <cstddef>

template <template<class> class S, typename T>
auto make_array() {
    return std::array<T, S<T>::value>{};
}

template <typename T> struct Specification;

template <>
struct Specification<int> {
    static constexpr size_t value = 42;
};

int main(){
  auto x = make_array<Specification,int>();  
}

请注意,为了清楚起见,我相当冗长。您可以使用std::integral_constant 节省一些打字时间,例如:

template <>
struct Specification<double> : std::integral_constant<size_t,3> {};

【讨论】:

  • 谢谢!这个解决方案允许我使用多种规格,而这正是我想要的。
  • 请注意,您在这里也不一定需要专业化:在此示例中,使用template &lt;typename T&gt; struct digits : std::integral_constant&lt;int, std::numeric_limits&lt;T&gt;::digits&gt; {}; 而不是变量模板将允许您完全按照您的意愿使用digits
  • @N.Shead OP 需要不同的规格。使用您的解决方案,您将如何将digits 的不同变体传递给make_array?无论如何,我认为您的评论针对错误的用户,这不是针对 OP 的吗?如果您有更好的解决方案,请发布答案。我还不明白您在评论中的代码如何回答问题
  • @idclev463035818 是的,那是为 OP 准备的。但我的假设是他们会在 digitssize : std::integral_constant&lt;int, sizeof(T)&gt; {} 等之间进行选择:不同的规范作为“变量模板”而不是作为“给定 T 的结果”的规范
  • @3Dave 我这样做是因为不久之后,SFINAE 仍然让我头疼,而 C++20 附带的大多数现代东西都比头疼更糟糕。几乎感觉一旦你掌握了窍门,做同样的事情就会完全不同;)
【解决方案2】:

作为idclev's answer 的后续行动,您可以通过继承例如integral_constant。例如,您想要的用法类似于

template <template <typename T> int Specification, typename T>
auto make_array() {
    return std::array<T, Specification<T>>;
}

template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;

// ...

auto foo = make_array<digits, double>();

但是,正如您所指出的,这是不可能的:您不能将变量模板作为模板模板参数传递。但是,通过将digits 直接转换为结构,您可以这样做:

template <template <typename T> class Specification, typename T>
auto make_array() {
    // we no longer have the guarantee of the type here, unfortunately
    // but you can use a static_assert to improve error messages
    // (also using `std::size_t` here for correctness)
    static_assert(
        std::is_convertible<decltype(Specification<T>::value), std::size_t>::value,
        "value must be convertible to size_t");
    
    return std::array<T, Specification<T>::value>;
}

// use a type rather than a variable template
// we just inherit from integral constant to save on some typing
// (though you could do this explicitly as well)
template <typename T>
struct digits : std::integral_constant<int, std::numeric_limits<T>::digits> {};

// ...

// same call!
auto foo = make_array<digits, double>();

【讨论】:

  • 你假设 OP 总是想要std::numeric_limits&lt;T&gt;::digits,如果是这样的话,除了T,你不需要传递任何东西给make_array,你可以直接在@987654330 中使用std::numeric_limits&lt;T&gt;::digits @。我想问题中关于std::numeric_limits&lt;T&gt;::digits 的部分只是变量模板的随机示例
  • @idclev463035818 我不这么认为?他们也可以改用struct size : std::integral_constant&lt;int, sizeof(digits)&gt; {}; 并致电make_array&lt;size, double&gt;(); — 没有什么能阻止他们?
  • 好的,对不起,我很困惑。但是你的答案和我的基本一样。仅当所需规范之一 std::numerical_limits&lt;T&gt;::digits 时,您才需要“手动”专门化。否则我看不出区别
  • @idclev463035818 是的。这就是为什么我把它作为评论留下:)
  • 好吧抱歉,有点固执;)。 cmets中的代码真的很难理解……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 2021-05-13
  • 2022-06-15
  • 2020-02-24
相关资源
最近更新 更多