0

这目前是伪代码,因为这是我在开始将其编写为完整代码之前正在研究的一个想法。

我知道我可以创建一个普通的可变参数函数,它使用va_argand va_listprintf()但是,我想完全避免使用它们。

我正在考虑使用模板化可变参数。我正在考虑使用可变参数制作一个可实例化的类模板。这里的条件是这个类的构造函数只能接受两种类型,但任何一种类型的数量都可以变化。我知道与编译器将如何解释以及调用它们的顺序相比,参数在代码中的写入位置是不可知的,但这不是问题。我为参数选择的顺序是出于可读性和一致性目的的约定。

下面是一个伪代码示例:

class TypeIn {...}
class TypeOut{...}

template<typename... T1, typename... T2>
class MyObject {
    std::array<TypeIn*> inputs_;
    std::array<TypeOut*> outputs_;
public:
    MyObject(T1&&... inputs, T2&& ... outputs) { ... }
};

由于我仍在使用 C++17 并且还没有包含概念、模块和协程的 C++20,因此确保它T1是一个类TypeInT2是一个TypeOut类的最干净、最可靠和最有效的方法是什么对象并相应地填充数组?我可以使用向量,但是一旦构造了对象,输入和输出的大小就不会改变。

可能的用例是:

using In = TypeIn;
using Out = TypeOut;
MyObject obj( In a, In b, In c, Out x, Out y);

如果可能的话,我不希望有这个语法:

MyObject<In,In,In,Out,Out> obj( In a, In b, In c, Out X, Out y);

因为第一个更干净或更易读。



编辑

在做了一些思考之后,我想知道这是否可以代替......

class In {...}
class Out{...}


// template<typename T1 = In, typename T2 = Out>
// T1 must == type In and T2 must == type Out
class MyObject {
private:
     std::vector<In*> inputs_;
     std::vector<Out*> outputs_;

public:
      MyObject() = deafault;

      template<typename... Inputs> // would probably use move semantics or forwarding
      void assignInputs(Inputs&& ... inputs);

      template<typename... Outputs> // would probably use move semantics or forwarding
      void assignOutputs(Inputs&& ... outputs);
};

但是,这将迫使用户必须构造对象,然后调用这两个函数......我试图在构造时完成所有这些......

4

3 回答 3

5

根据您的评论,我猜您正在寻找类似的东西

template<typename T1, typename T2>
class MyObject {
    std::vector<T1> inputs_;
    std::vector<T2> outputs_;
public:
    MyObject(std::initializer_list<T1> inputs, std::initializer_list<T2> outputs)
      : inputs_(inputs), outputs_(outputs) { }
};

用作

MyObject obj({a, b, c}, {x, y});

请注意,这要求这两种类型是可复制构造的。它不适用于仅移动类型。


如果您真的坚持将元素单独作为构造函数参数,这在技术上是可行的,但是实现起来会更加复杂,而且几乎没有什么好处。

于 2020-03-02T13:52:07.300 回答
2

我试图在施工时完成所有这些......

恕我直言,基于初始值设定项列表的解决方案(参见 walnut 的答案)更可取,而且从调用者的角度来看,哪些参数是输入值,哪些是输出值更清楚。

但是...只是为了好玩...如果您真的想在构造上做所有事情并避免对参数进行分组(或也将它们混合)...使用std::tuple, std::tuple_cat(), 委托构造函数std::apply()if constexpr

#include <iostream>
#include <type_traits>
#include <tuple>
#include <vector>

struct TypeIn  {};
struct TypeOut {};

struct MyObject
 {
   template <typename TargetType, typename T>
   static auto filterVal (T && t)
    {
      if constexpr ( true == std::is_same_v<TargetType, std::decay_t<T>> )
         return std::tuple<TargetType>{std::forward<T>(t)};
      else
         return std::tuple<>{};
    }

   std::vector<TypeIn> is;
   std::vector<TypeOut> os;

   template <typename ... It, typename ... Ot>
   MyObject (std::tuple<It...> && ti, std::tuple<Ot...> && to)
    : is{ std::apply([](auto && ... ts){
                        return std::vector<TypeIn>{
                           std::forward<decltype(ts)>(ts)... }; }, ti) },
      os{ std::apply([](auto && ... ts){
                        return std::vector<TypeOut>{
                           std::forward<decltype(ts)>(ts)... }; }, to) }
    { }

   template <typename ... Ts>
   MyObject (Ts && ... ts)
    : MyObject{std::tuple_cat( filterVal<TypeIn>(std::forward<Ts>(ts)) ... ),
               std::tuple_cat( filterVal<TypeOut>(std::forward<Ts>(ts)) ... )}
    { }
 };

int main ()
 {
   TypeIn   a, b, c, d;
   TypeOut  e, f;

   MyObject mo{ a, b, c, d, e, f };

   std::cout << mo.is.size() << " TypeIn vals\n"
             << mo.os.size() << " TypeOut vals\n";
 }

我再说一遍:只是为了好玩。

于 2020-03-02T15:32:58.830 回答
-1

在阅读了评论和提供的答案之后,由于语言本身的性质,我得出的结论是,由于类型的数量是固定的并且是已知的,但它们的数量并不意味着甚至不需要模板。 .. 它可以简单地归结为具有简单构造函数和移动语义的基本类,同时使用 initialize_list。

class In{...};
class Out{...};

class MyObject {
private:
     std::vector<In> inputs_;
     std::vector<Out> outputs_;

public:
     MyObject(initializer_list<In> inputs, initializer_list<Out> outputs ) :
       inputs_( std::move(inputs) ),
       outputs_( std::move(outputs) )
     {}
};

编辑- 我并没有完全试图展示可以编译的代码,它只是为了说明一点,但是,我修复了它以正确匹配未来的读者。

于 2020-03-02T14:08:51.143 回答