Есть ли хороший способ получить идеальную пересылку для функций внутри шаблонного класса? В частности, в кодеИдеальная пересылка функций внутри шаблонного класса C++
#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
и нам нужны функции, которые согласуются с этими данными. Конечно, typechecker поймает несоответствия, но эта система не так проста, как просто наличие одного аргумента шаблона, T
.
В принципе, мне интересно, есть ли лучший способ сделать это или мы застряли, просто шаблонизируя каждую функцию в классе отдельно?
Мне нравится писать короткие обертки, как 'аннулируются Foo (T сопзИ & х)' и 'ничтожной Foo (T && х)' что оба вызова (с помощью 'std :: move' в последнем случае) - частный внутренний метод, который использует совершенную пересылку (' template void foo (U && x) ') и выполняет реальную работу. Таким образом, ваш открытый API является явным и ломает ошибки раньше, но почти нет дублирования в частных интранах. –
Cameron
Для функции, которая просто наблюдает за значением своих аргументов (и не изменяет их и не повышает эффективность, потенциально крадя их ресурсы), параметр 'const &' и безупречная пересылка по-прежнему являются жизнеспособным и часто лучшим решением. – dyp