mardi 14 avril 2015

Perfect forwarding for functions inside of a templated C++ class

Is there a good way to get perfect forwarding for functions inside of a templated class? Specifically, in the code



#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);
}


Uncommenting the last bit of code, we get the error:



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


Now, we can fix this problem by moving the template argument inside of the class:



#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);
}


However, this seems like it'll be really verbose. For example, imagine that we have a large number of functions that all depended on the type T and needed perfect forwarding. We'll need separate template declarations for each. Also, the class Foo may contain a data of type T and we want functions that are consistent with this data. Certainly, the typechecker will catch mismatches, but this system isn't as straightforward as just having a single template argument, T.


Basically, what I'm wondering is if there's a better way to do this or are we stuck just templating each function in the class separately?


Aucun commentaire:

Enregistrer un commentaire