C ++中的装饰模式与模板

时间:2017-01-15 00:56:41

标签: c++ templates design-patterns variadic-templates

我试图通过复数视频来理解这个例子(我已经修改了一点):

#include <iostream>
#include <functional>

//this next line is partial specialization, but I don't understand why it's necessary
template<typename> struct Logger;

template <typename R, typename... Args>
struct Logger<R(Args...)>
{
    std::function<R(Args...)> func_;
    std::string name_;

    Logger(const std::function<R(Args...)>& func, const std::string& name):
        func_{func}, name_{name} {}

    R operator() (Args... args)
    {
        std::cout << "Entering " << name_ << std::endl;
        R result = func_(args...);
        std::cout << "Exiting " << name_ << std::endl;
        return result;
    }
};

// this is how the example was originally written.  It uses a function pointer as an argument which is ok except you can't give it a lambda.  
template <typename R, typename... Args>
auto make_logger(R (*func)(Args...), const std::string& name) ->Logger<R(Args...)>
{
    return Logger<R(Args...)>{std::function<R(Args...)>(func), name};
}

//this is my attempt to make it so it will accept a lambda but it doesn't work.
template <typename R, typename... Args>
auto make_logger2(std::function<R(Args...)> func, const std::string& name) ->Logger<R(Args...)>
{
    return Logger<R(Args...)>{std::function<R(Args...)>(func), name};
}

double add(double a, double b, double c)
{
    std::cout << a  << " + " << b << " + " << c << " = " << a + b + c << std::endl;

    return a + b + c;
}

int main()
{

    auto logged_add = make_logger(add,"Add");
    auto result = logged_add(2,3,5);

//    auto lm  = [](std::string str1, std::string str2){return str1 + str2;};
//    auto logged_string = make_logger2(lm, "string add");
//    auto result2 = logged_string("Hello ", "World!");

    //I get the following compile error if the next two lines are uncommented:
    // main.cpp:101: error: no matching function for call to 'make_logger2(main()::<lambda(std::__cxx11::string, std::__cxx11::string)>, const char [11])'

    //auto logged_string = make_logger2([](std::string str1, std::string str2){return str1 + str2;},"string add");
    //auto result2 = logged_string("Hello ", "World!");

    std::cout << "result = " << result << std::endl;
    //std::cout << "result2 = " << result2 << std::endl;

    return 0;
}

我的两个主要问题是为什么部分特化并且可以修改代码以便它也可以使用lambda?

2 个答案:

答案 0 :(得分:3)

  

为什么部分专业化?

目的是提供一个函数类型,但也可以从中返回返回类型和参数的类型 您只能通过部分专业化来实现,即以下内容:

template<typename> struct S;

template<typename R, typename... Args>
struct S<R(Args...)> { /* ... */ };

如您所见,主模板没有定义,因为在这种情况下根本不需要它。
无论如何,请注意它只接受一种类型。目的是按照以下方式使用它:S<void(int, char)>
你怎么知道返回类型是voidintchar是主要模板中的参数?你不能,但你在专业化范围内。

  

是否可以修改代码以便它也可以使用lambda?

你甚至可以摆脱std::function和函数指针,以便拥有一个基于lambda的解决方案。使用lambdas可以轻松捕获或调用其他函数。

这是一个最小的工作示例:

#include <iostream>
#include<type_traits>
#include <utility>

template <typename F>
struct Logger: F
{
    std::string name_;

    Logger(F &&func, const std::string& name):
        F{std::move(func)}, name_{name} {}

    template<typename... Args>
    auto operator() (Args&&... args)
    {
        std::cout << "Entering " << name_ << std::endl;
        auto result = F::operator()(std::forward<Args>(args)...);
        std::cout << "Exiting " << name_ << std::endl;
        return result;
    }
};

template <typename F>
auto make_logger(F &&f, const std::string& name)
{
    return Logger<std::decay_t<F>>{std::move(f), name};
}

double add(double a, double b, double c)
{
    std::cout << a  << " + " << b << " + " << c << " = " << a + b + c << std::endl;
    return a + b + c;
}

int main()
{
    auto logged_add = make_logger([](auto... args) { return add(args...); }, "Add");
    auto result = logged_add(2,3,5);
}

答案 1 :(得分:2)

以下代码将接受函数指针和lambdas,其中可变参数被移动operator()并且不再需要特化。

template <typename Func>
struct Logger
{
    Func func_;
    std::string name_;

    Logger(Func&& func, const std::string& name):
        func_{std::move(func)}, name_{name} {}

    template<typename... Args>
    decltype(auto) operator() (Args&&... args)
    {
        std::cout << "Entering " << name_ << std::endl;
        decltype(auto) result = func_(std::forward<Args>(args)...);
        std::cout << "Exiting " << name_ << std::endl;
        return result;
    }
};

template <typename Func>
decltype(auto) make_logger(Func&& func, const std::string& name)
{
    return Logger<std::decay_t<Func>>{std::move(func), name};
}

live demo