完美转发模板化C ++类中的函数

时间:2015-04-14 22:42:28

标签: c++ templates c++11 c++14

是否有一种很好的方法可以完美转发模板类中的函数?具体来说,在代码中

#include <iostream>

// Forward declare a Bar
struct Bar;

// Two different functions that vary based on the kind of argument
void printme(Bar const & bar) {
    std::cout << "printme: constant reference bar" << std::endl;
}
void printme(Bar && bar) {
    std::cout << "printme: r-value reference bar" << std::endl;
}

void printme2(Bar const & bar) {
    std::cout << "printme2: constant reference bar" << std::endl;
}
void printme2(Bar && bar) {
    std::cout << "printme2: r-value reference bar" << std::endl;
}

// Some class with a bunch of functions and possible some data (though, not
// in this one)
template <typename T>
struct Foo {
    void baz(T && t) {
        printme(std::forward <T> (t)); 
    }
    void buz(T && t) {
        printme2(std::forward <T> (t)); 
    }
};

struct Bar {};

int main() {
    Foo <Bar> foo;        
    foo.baz(Bar());

    // Causes an error
    Bar bar;
    //foo.buz(bar);
}

取消注释最后一位代码,我们得到错误:

    test03.cpp: In function 'int main()':
    test03.cpp:51:16: error: cannot bind 'Bar' lvalue to 'Bar&&'
         foo.buz(bar);
                    ^
    test03.cpp:30:10: note: initializing argument 1 of 'void Foo<T>::buz(T&&) [with T = Bar]'
         void buz(T && t) {
              ^
    Makefile:2: recipe for target 'all' failed
    make: *** [all] Error 1

现在,我们可以通过在类中移动模板参数来解决这个问题:

#include <iostream>

// Forward declare a Bar
struct Bar;

// Two different functions that vary based on the kind of argument
void printme(Bar const & bar) {
    std::cout << "printme: constant reference bar" << std::endl;
}
void printme(Bar && bar) {
    std::cout << "printme: r-value reference bar" << std::endl;
}

void printme2(Bar const & bar) {
    std::cout << "printme2: constant reference bar" << std::endl;
}
void printme2(Bar && bar) {
    std::cout << "printme2: r-value reference bar" << std::endl;
}

// Some class with a bunch of functions and possible some data (though, not
// in this one)
template <typename T>
struct Foo {
    void baz(T && t) {
        printme(std::forward <T> (t)); 
    }
    template <typename T_>
    void buz(T_ && t) {
        printme2(std::forward <T_> (t)); 
    }
};

struct Bar {
    Bar() {} 
};

int main() {
    Foo <Bar> foo;        
    foo.baz(Bar());

    Bar bar;
    foo.buz(bar);
}

然而,这似乎真的很冗长。例如,假设我们有大量的函数都依赖于类型T并且需要完美的转发。我们需要为每个模板声明单独的模板声明。此外,类Foo可能包含T类型的数据,我们需要与此数据一致的函数。当然,类型检查器会捕获不匹配,但是这个系统并不像只有一个模板参数T那么简单。

基本上,我想知道的是,是否有更好的方法可以做到这一点,还是我们只是单独模仿课堂上的每个功能?

1 个答案:

答案 0 :(得分:4)

模板非常不同。

在第一个代码中,您的模板参数为Bar,因此我甚至不确定它在做什么,因为除了reference-qualified-types之外,您不应该使用std::forward。我认为它是void buz(Bar&& t) {printme((Bar)t);},并且编译器不愿意将bar中的左值main传递给期望Bar&&的函数。

在第二个代码块中,由于通用引用,模板参数为Bar&,因此代码为void buz(Bar& t) {printme((Bar&)t);},其绑定到bar中的左值main正好。

如果您想要完美转发,模板参数必须是您要传递的rvalue限定类型,这意味着您几乎总是必须使该函数本身被模板化。但是,请帮个忙,并将其命名为不同。 TT_不同类型

template <typename U>
void buz(U&& t) {
    printme2(std::forward<U>(t)); 
}

如果你想要SFINAE,你也可以添加它:

template <typename U,
    typename allowed=typename std::enable_if<std::is_constructible<Bar,U>::value,void*>::type
    >
void buz(U && t) {
    printme2(std::forward <U> (t)); 
}