【问题标题】:Best practice for passing multiple function arguments in C++ [closed]在 C++ 中传递多个函数参数的最佳实践 [关闭]
【发布时间】:2023-03-08 07:16:01
【问题描述】:

我想知道在 C++ 中传递多个函数参数的最佳做法是什么。我想遵循在 STL 中看到的适当的通用设计原则。

我有一个通用算法函数。使用模板,它允许客户端选择将用于运行算法的数据结构以及将从哪个容器实现中提取数据。但是,为了使其可重用,我选择了一些应插入客户端设计代码的位置。

template<class CustomContainerIterator, class Queue, class Element>
void run_generic_algorithm(CustomContainerIterator container,
                           std::function<void(Element)> client_code1,
                           std::function<void(Element)> client_code2,
                           std::function<void(Element)> client_code3)
{
    Queue queue;
    //Push things iterated through onto the queue
    Element queue_element = queue.top();
    client_code1(queue_element);
    // Run code modifying queue
    client_code2(queue_element);  
    // Run code modifying queue
    client_code3(queue_element);    
}

client_code() 函数由客户端指定,并且可以修改客户端拥有的相同变量。在我的代码中,有 5 个客户端函数。我最初将它们全部作为 lambda 表达式传递并捕获了我想要修改的客户端变量。但是,这会导致非常冗长的函数调用。

大多数时候我什至没有指定所有客户端函数,所以我必须放入一个什么都不做的 lambda 函数。有没有更好的方法来实现我想要的。我想坚持正确的通用编程原则吗?


好的,我已经阅读了答案。我可以结合这样的想法吗?现在我觉得我不需要虚函数了。

template <typename Element>
struct Customization {
    void client_code1(Element) {}
    void client_code2(Element) {}
    void client_code3(Element) {}
    void client_code4(Foo) {}
    void client_code5(Bar) {}
};

template <typename Element>
struct Implementation : public Customization {
    Data some_data;
    void client_code2(Element) {
        //Fill some_data with information.
    }
};

template<class Queue, class CustomContainerIterator, class CustomImplementation>
void run_generic_algorithm(CustomContainerIterator container,
                           CustomImplementation implementation)
{
    Queue queue;
    //Push things iterated through onto the queue
    auto queue_element = queue.top();

    implementation.client_code1(queue_element);
    // Run code modifying queue
    implementation.client_code2(queue_element);  
    // Run code modifying queue
    implementation.client_code3(queue_element);    
}

有用法

std::vector<int> v;
Implementation <int> custom;
run_generic_algorithm<std::queue<int>>(v, custom);

【问题讨论】:

  • 如果你想遵循 stl 那么你的方法应该使用迭代器而不是容器。关于客户端功能,您应该显示代码而不是用文字描述它
  • 你是对的。我在写这篇文章时犯了一个错误。实际代码确实将迭代器带到自定义容器。
  • 恕我直言,您将代码简化得太多了。该方法不使用其参数,并且queue_elementclient_code 的来源并不明显
  • pop 返回void 无论如何queue 在那一点上是空的
  • 为什么队列会是空的?我只弹出了一个元素。

标签: c++ templates lambda generic-programming


【解决方案1】:

有 5 个客户端函数。

你可以改为传递结构

template <typename Element>
struct Customization {
    std::function<void(Element)> client_code1;
    std::function<void(Element)> client_code2;
    std::function<void(Element)> client_code3;
    std::function<void(Foo)> client_code4;
    std::function<void(Bar)> client_code5;
};

甚至可能是一个接口:

template <typename Element>
struct Customization {
    virtual ~Customization() = default;
    virtual void client_code1(Element) = 0;
    virtual void client_code2(Element) = 0;
    virtual void client_code3(Element) = 0;
    virtual void client_code4(Foo) = 0;
    virtual void client_code5(Bar) = 0;
};

然后

template<class Range, class Element>
void run_generic_algorithm(Range& r, const Customization <Element>& customization);

大多数时候我什至没有指定所有客户端函数,所以我必须放入一个什么都不做的 lambda 函数。

使用先前的注释,您可以为先前结构的成员提供默认值。

否则,您可以为参数添加默认值或创建重载。

【讨论】:

  • 我对 C++ 中的继承不是很熟悉。为什么它们需要是虚拟的。
  • virtual允许派生类override基类的方法。
  • 谢谢。我现在明白了。
  • 新的实施会奏效吗?
  • @Halbort:我在您的新实现中修正了一些错字并给出了示例用法。它应该可以工作。
【解决方案2】:

在与您描述的情况类似的情况下,我已将客户端函数放入类/结构中,并将该类作为模板参数或作为实例传递。 (如果函数从通用函数外部共享状态,则只需要传递一个类对象。)

我发现将函数放在一个类中还可以更轻松地记录函数在泛型函数中的职责。

template< class Queue >
class a_traits{
public:
   static void client_code1( Queue & );
   static void client_code2();
   static void client_code3();

};
template <class Container, class Queue, class Traits>
void run_generic_algorithm(const Container& container) {
  Queue queue;
  Traits::client_code1(queue_element);
  // Run code modifying queue
  Traits::client_code2();  
  // Run code modifying queue
  Traits::client_code3();    
}

【讨论】:

  • 哦,谢谢。没想过通过一门课。
  • 您可能需要重新排序模板参数以推导出容器。
  • 将 Traits 作为参数传递将允许具有带有状态的特征。
  • 另请注意,通过传递这样的类可以使编译器更容易进行函数内联。将函数作为对象传递或使用虚函数会使内联变得更加困难。
猜你喜欢
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
  • 2018-05-10
  • 2021-04-18
  • 2011-10-18
  • 1970-01-01
相关资源
最近更新 更多