在不违反DRY原则的情况下,对传递的函数对象的参数类型进行模板选择

时间:2017-09-07 16:14:32

标签: c++ templates c++14 enable-if

这里我首先介绍模板函数over(vec, f)的两个变体。

两个版本都迭代一个类似矢量的对象,并为每个元素调用一个函数对象。

一个版本使用两个参数调用函数对象 - 元素引用和索引 - 第二个只使用元素引用。

这个想法是让编译器选择与传入的lambda匹配的版本,这样用户就可以在lambda签名中表达意图,而不必选择不同名称的自由函数。

这是代码:

#include <vector>
#include <iostream>

template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;


template<class Vector, class F>
auto over(Vector &&vec, F &&f)
-> void_t<decltype(f(vec.operator[](std::declval<std::size_t>()), std::declval<std::size_t>()))>
{
    const auto size = vec.size();
    for (std::size_t i = 0; i < size; ++i) {
        f(vec[i], i);
    }
}


template<class Vector, class F>
auto over(Vector &&vec, F &&f)
-> void_t<decltype(f(*vec.begin()))>
{
    for (auto &&x : vec) {
        f(x);
    }
}

int main() {
    std::vector<float> vf = {1.0, 1.1, 1.2};

    std::cout << "two-argument form:\n";
    over(vf, [](auto &&val, auto &&index) {
        std::cout << index << " : " << val << std::endl;
    });

    std::cout << "\none-argument form:\n";
    over(vf, [](auto &&val) {
        std::cout << val << std::endl;
    });
}

问题:

您将看到void_t<>返回类型生成器中的子句知道该函数的实现。我对此感到不满:

a)它在界面中泄露了实现细节,

b)它不是干的。

有没有更好的方法来实现这一目标:

a)允许在不更改模板启用程序

的情况下更改实现

b)看起来我的狗在我的键盘上打得不合适?

3 个答案:

答案 0 :(得分:2)

对于这个例子,避免“重复”将比重复本身更多的工作/复杂性,但基本的想法是计算函数的优点,然后适当地调度。这里讨论一个非常类似的问题:Call function with part of variadic arguments。使用metricName的实现,你可以实现一个名为dispatch的函数(我在回答这个问题时称之为foo):

MyEvaluator

此答案也符合14条要求。实例:http://coliru.stacked-crooked.com/a/14750cef6b735d7e

编辑:此方法不适用于通用lambdas。所以另一种方法是以这种方式实现调度:

Evaluator

答案 1 :(得分:1)

在C ++ 17中,您可以使用基于std::is_invocable的SFINAE,类似于:

template <class Vector, class F>
std::enable_if_t<std::is_invocable<F,
                                   typename Vector::value_type,
                                   std::size_t>::value>
over(const Vector& vec, F&& f)
{
    const auto size = vec.size();
    for (std::size_t i = 0; i < size; ++i) {
        f(vec[i], i);
    }
}

template <class Vector, class F>
std::enable_if_t<std::is_invocable<F, typename Vector::value_type>::value>
over(const Vector& vec, F&& f)
{
    const auto size = vec.size();
    for (const auto& e : vec) {
        f(e);
    }
}

答案 2 :(得分:0)

好的,这是我的第一次认真尝试。

有没有比这更好的东西?

    let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
    let firstViewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController") as? FirstViewController
    let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
    let thirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as? ThirdViewController

    self.navigationController?.setViewControllers([firstViewController,secondViewController,thirdViewController], animated: false)

http://coliru.stacked-crooked.com/a/cab94488736b75ed