std :: forward和operator()

时间:2016-06-13 00:22:31

标签: c++ templates

我一直在为我的C ++项目编写一个static_if,我偶然发现了以下代码:

#include <iostream>
using namespace std;

namespace static_if_detail {

struct identity {
    template<typename T>
    T operator()(T&& x) const {
        return std::forward<T>(x);
    }
};

template<bool Cond>
struct statement {
    template<typename F>
    void then(const F& f){
        f(identity());
    }

    template<typename F>
    void else_(const F&){}
};

template<>
struct statement<false> {
    template<typename F>
    void then(const F&){}

    template<typename F>
    void else_(const F& f){
        f(identity());
    }
};

} //end of namespace static_if_detail

template<bool Cond, typename F>
static_if_detail::statement<Cond> static_if(F const& f){
    static_if_detail::statement<Cond> if_;
    if_.then(f);
    return if_;
}

template<typename T>
void decrement_kindof(T& value){
    static_if<std::is_same<std::string, T>::value>([&](auto f){
        f(value).pop_back();
    }).else_([&](auto f){
        --f(value);
    });
}


int main() {
    // your code goes here
    std::string myString{"Hello world"};
    decrement_kindof(myString);
    std::cout << myString << std::endl;
    return 0;
}

这对我来说都是有意义的,除了一件事:struct identity中的重载operator()。它采用类型为T的rhs,称为x,cool和all。但是当调用identity时,实际上没有任何内容传递给身份。

template<typename F>
void then(const F& f){
    f(identity());
}

上面,f呼叫身份,但不传递身份。然而,identity返回转发的参数(在我的例子中,是一个std :: string),并弹出字符串的最后面的字符。 当身份没有传递给它的参数转发时,身份如何返回转发的参数?

3 个答案:

答案 0 :(得分:4)

f无法调用identity - f,其实例为identity。在这里走过两个案例:

static_if<std::is_same<std::string, T>::value>([&](auto f){
    f(value).pop_back();
}).else_([&](auto f){
    --f(value);
});

如果Tstd::string,那么我们实例化一个statement<true>,其then()使用identity实例调用传入函数。第一个lambda f的参数将是identity类型 - 所以f(value)实际上只是value而我们value.pop_back()

如果T不是std::string,那么我们实例化statement<false> then()什么也不做,else_()调用lambda,实例为{ {1}}。同样identity只是f(value)而我们value

这是--value的一个令人困惑的实现,因为lambda中的static_if始终是f。这样做是必要的,因为我们无法直接使用identity(无法写value,因为那里没有依赖名称,所以编译器会愉快地确定因为整数格式不正确,所以我们只是将value.pop_back()的所有用法包含在依赖函数对象中以延迟实例化(value依赖于{{1因此,在提供f(value)之前不能实例化 - 如果函数未被调用则不会发生这种情况。

最好实现它,以便实际将参数传递给lambda。

答案 1 :(得分:1)

template<typename F>
void then(const F& f){
  f(identity());
}

更具可读性

template<typename F>
void then(const F& f){
  f(identity{});
}

他们正在构建一个身份对象,而不是调用一个。

这里的技巧是,即使函数从未实例化,模板函数的非依赖部分也必须有效。

所以当值为整数时,value.pop_back()在lambda 中永远不会有效。

identity{}传递给thenelse个案件中的一个,我们可以避免此问题。

语句f(value)生成一个依赖类型。所以它只需要在实际实例化lambda的模板operator()时才有效(必须有一些使它有效的可能f,但这是一个极端情况)。

由于我们只实例化条件告诉我们的路径,f(value)几乎可以以任何我们想要的方式使用,只要它在被采用的分支中有效。

我会称f为更好的名称,例如safeguardvarmagic,而不是f。在简洁代码中使用两个f的不同上下文会增加混淆。

答案 2 :(得分:0)

让我们来看一下Condtruestatic_if的情况,因此,将使用主模板类...

template<bool Cond>
struct statement {
    template<typename F>
    void then(const F& f){
        f(identity());
    }

    template<typename F>
    void else_(const F&){}
};

回想一下,你的呼叫功能是:

static_if<std::is_same<std::string, T>::value>
(
   [&](auto f){ //This is the lamda passed, it's a generic lambda
        f(value).pop_back();
    }
).else_(
   [&](auto f){
        --f(value);
    }
);

在下面的应用功能中,F通用lambda 的一种类型(意思是,您可以调用f任何类型)

template<typename F>
void then(const F& f){
    f(identity());
}

identity()创建一个identity类型的对象,然后将其作为参数传递给调用您的通用lambda。

 [&](auto f){ //This is the lamda passed, it's a generic lambda
        f(value).pop_back();
    }

但请回想一下,fidentity类型的对象,并且有一个模板化调用()运算符,它基本上返回传递给它的对象。

所以,我们这样做:

void decrement_kindof(std::string& value){
    static_if<std::is_same<std::string, std::string>::value>([&](auto f){
        f(value).pop_back();
    }).else_([&](auto f){
        --f(value);
    });
});

缩减为:

void decrement_kindof(std::string& value){
    static_if<true>([&](auto f){
        f(value).pop_back();
    }).else_([&](auto f){
        --f(value);
    });
});

缩减为:

void decrement_kindof(std::string& value){
    static_if<true>(
        [&](identity ident){
             auto&& x = ident(value); //x is std::string()
             x.pop_back();
       } (identity())  //<-- the lambda is called

    ).else_(
        [&](auto f){    //This is not evaluated, because it's not called by the primary template of `statement`
            --f(value);
        }
    );   
});