简体   繁体   English

使用可变数量的浮点参数

[英]Using variable number of float arguments

I'm trying to implement a flexible constructor for my struct Polynomial : 我正在尝试为我的struct Polynomial实现一个灵活的构造函数:

struct Polynomial
{
    std::vector<float> coefficients;
    size_t degree;
};

The degree of the polynomial is variable. 多项式的程度是可变的。 What I would like is to have a constructor like 我想要的是拥有像这样的构造函数

Polynomial(float... _coefficients);

I've tried variadic template : 我尝试过variadic模板:

template<float... Args>
Polynomial(Args... args);

But float is a non-type, so I've done : 但浮动是非类型的,所以我做了:

template<typename... Args>
Polynomial(Args... args);

But this allow my coefficients to be anything, not realy what I want. 但是这允许我的系数是任何东西,而不是我想要的。 I know I could use : 我知道我可以使用:

Polynomial(size_t _degree, ...);

But it is really unsafe. 但它真的不安全。

At the moment I'm using : 目前我正在使用:

Polynomial(std::vector<float>);

But this force the call to be like : 但这迫使调用如下:

Polynomial P({f1, f2, f3}); // with fn floats

So I would like to know if there is a good way to do this. 所以我想知道是否有一个很好的方法来做到这一点。

Thank you ! 谢谢 !

You can use initializer_list : 你可以使用initializer_list

#include <vector>
#include <initializer_list>

struct Polynomial {
    std::vector<float> coeffs;
    std::size_t degree;

    Polynomial(std::initializer_list<float> init)
        : coeffs{ init }, degree(init.size()) { }
};

int main() {
    Polynomial p{ 1, 2, 3. };
}

Answering your question 回答你的问题

I would like to know if there is a good way to do this 我想知道是否有一个很好的方法来做到这一点

Yes, i think the way you are doing it it's more than acceptable. 是的,我认为你这样做的方式不仅仅是可以接受的。 And even the syntax where you use it Polynomial P({f1, f2, f3}); 甚至你使用它的语法Polynomial P({f1, f2, f3}); isn't so ugly at all. 根本不是那么丑陋。

Furthermore, use std::vector is as efficient as variadic and much more comprehensible for others. 此外,使用std::vector与可变参数一样高效,对其他人来说更容易理解。

With the variadic approach, you will find difficult to force those types received to be float , but with std::vector you got it controlled 使用可变方法,你会发现难以强制接收的那些类型是float ,但是使用std::vector你可以控制它

I think that your way (a vector parameter, or better (IMHO) an initializer list) is a good way. 我认为你的方式(矢量参数,或更好的(恕我直言)初始化列表)是一个好方法。

Another way (simple but with drawbacks) could be the use of narrowing to be sure that Args... are float or types that can be narrowed to float . 另一种方式(简单但有缺点)可能是使用缩小来确保Args...float或可以缩小到float类型。 Something like 就像是

struct Polinomial
 {
   std::vector<double>  v;
   std::size_t          degree;

   template <typename ... Args>
   Polinomial (Args const & ... args)
      : v { float{args}... }, degree { sizeof...(Args) }
    { }
 };

It's simple and works, by example 举例来说,这很简单,也很有效

Polinomial p { 2.3f, 3.5f, 6.7f };

but your constructor doesn't accept, by example, integer or double or long double values; 但是你的构造函数不接受整数或double精度或double long double值; so 所以

Polinomial p { 2.3f, 3.5f, 6.7 };
// ........................^^^  double, error

Polinomial p { 2.3f, 3.5f, 6 };
// ........................^  int, error

and probably is too restrictive. 而且可能限制太多了。

You can use a recursive template parameter processing. 您可以使用递归模板参数处理。 The general idea is to use a private method that adds first parameter to the coefficient vectors and recurse with other parameters until they have all been processed: 一般的想法是使用一个私有方法,将第一个参数添加到coefficient向量并与其他参数一起递归,直到它们都被处理完毕:

struct Polynomial
{
    template<class...Args>
    Polynomial(Args... coeffs) {
        init(coeffs...);
        degree = coefficients.size() - 1;
    }
    std::vector<float> coefficients;
    size_t degree;
private:
    void init(float coeff) {
        coefficients.push_back(coeff);
    }
    template<class...Args> void init(float first, Args...others) {
        init(first);
        init(others...);
    }
};

You have many options. 你有很多选择。 You might want to look at the constructors of std::vector for inspiration. 您可能希望查看std::vector的构造函数以获取灵感。 For example a templated constructor that takes two iterators is very flexible: 例如,带有两个迭代器的模板化构造函数非常灵活:

template<typename T>
Polynomial(T begin, T end) : coefficients(begin, end), degree(coefficients.size()) {}

auto init = std::vector<float>{2.0, 1.0, 4.0};
Polynomial p2{init.begin(), init.end()};

Or you could take a std::initializer_list<float> as Jodocus suggested. 或者你可以像Jodocus建议的那样采用std::initializer_list<float>

You could have a templated constructor that takes any container type: 你可以有一个带有任何容器类型的模板化构造函数:

template<typename T>
Polynomial(T container) 
: coefficients(begin(container), end(container))
, degree(coefficients.size()) {}

auto init = std::vector<float>{2.0, 1.0, 4.0};
Polynomial p2{init};

Live demo . 现场演示

Or you could provide a combination of different constructors to suit different needs. 或者您可以提供不同构造函数的组合以满足不同的需求。

The way you are doing it is a "good way". 你这样做是一种“好方法”。 Imagine the caller wants to pass 100 coefficients. 想象一下,调用者希望传递100个系数。 If you use variadics you force the caller to do something like that: 如果使用可变参数,则强制调用者执行以下操作:

float c1,c2,c3,....c100;
// init them
Polynomial P(c1, c2, c3,....,c100);

If I was to use 100 coefficients I would certainly store them in a vector, and it would be rather cumbersome to pass them to your polynomial: 如果我要使用100个系数,我肯定会将它们存储在一个向量中,将它们传递给多项式会相当麻烦:

auto coeffs = std::vector<float>(100);
Polynomial P(coeffs[0],coeffs[1],....,coeffs[100]);

If however, you accept a vector, the caller can do both without pain: 但是,如果您接受一个向量,则调用者可以毫无痛苦地执行这两个操作:

Polynomial P({c1,c2,c2});
Polynomial P(coeffs);

On the other hand, using std::vector but not allowing a different container is an arbitrary restriction. 另一方面,使用std::vector但不允许不同的容器是任意限制。 The better way then would be to accept iterators, and let the caller do: 接下来更好的方法是接受迭代器,并让调用者执行:

Polynomial P(coeffs.begin(),coeffs.end());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM