Я хочу передать параметр (ы) (какого-то конкретного типа, скажем, int
) к функции-члену по r- или l-значению (const). Мое решение:
#include <type_traits>
#include <utility>
struct F
{
using desired_parameter_type = int;
template< typename X, typename = typename std::enable_if< std::is_same< typename std::decay< X >::type, desired_parameter_type >::value >::type >
void operator () (X && x) const
{
// or even static_assert(std::is_same< typename std::decay< X >::type, desired_parameter_type >::value, "");
std::forward< X >(x); // something useful
}
};
Еще один пример здесь http://pastebin.com/9kgHmsVC.
Но это слишком многословно. Как это сделать проще?
Может быть, я должен использовать суперпозицию std::remove_reference
а также std::remove_const
вместо std::decay
, но здесь есть только упрощение.
Если я правильно понимаю ваш вопрос, вы хотите иметь одну функцию, параметр которой является либо ссылкой на rvalue (в случае, если указано значение rvalue), либо ссылкой на lvalue для const
(в случае предоставления lvalue).
Но что бы сделала эта функция? Ну, так как он должен уметь обрабатывать оба случая, включая случай, когда предоставляется lvalue, он не может изменить свой ввод (по крайней мере, тот, который связан с x
параметр) — если бы он сделал, это нарушило бы семантику const
ссылка.
Но с другой стороны, если он не может изменить состояние параметра, нет никаких оснований для разрешения ссылки на rvalue: лучше пусть x
быть lvalue-ссылкой на const
все время. lvalue ссылки на const
может связываться с rvalues, поэтому вам будет разрешено передавать как rvalues, так и lvalues.
Если семантика функции отличается в зависимости от того, что передано, то я бы сказал, что имеет смысл написать два такие функции: одна, которая принимает ссылку на rvalue, а другая — на ссылку lvalue для const
,
Как упоминал Энди, здесь важно то, что вы могли бы на самом деле делать внутри вашей функции, которая имела бы смысл.
template <typename T> blah (T && x)
,blah (const int & x)
а также blah (int && x)
,Я предполагаю, что вы, должно быть, пытаетесь сделать первый вариант, и вы пытаетесь сделать любые возможные ошибки компилятора более удобными для пользователя. Ну, я бы сказал, что оно того не стоит; программист все равно увидит список «by…» в выводе любого приличного компилятора.
На самом деле, это очень хороший вопрос. До сих пор я также использовал универсальный справочный трюк плюс enable_if
молоток. Здесь я представляю решение, которое не использует шаблоны и использует lvalue cast в качестве альтернативы.
Ниже приведен реальный пример, когда ситуация возникает, используя известный пример на месте ofstream
использование, которое невозможно (или очень сложно) в C ++ 98 (я использую ostringstream
в примере, чтобы было понятнее).
Сначала вы увидите функцию со ссылкой на lvalue, как это часто встречается в C ++ 98.
#include<iostream>
#include<sstream>
struct A{int impl_;};
std::ostringstream& operator<<(std::ostringstream& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // possibly much longer code.
return oss;
}
// naive C++11 rvalue overload without using templates
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // ok, but there is code repetition.
return oss;
}
int main() {
A a{2};
{// C++98 way
std::ostringstream oss;
oss << a;
std::cout << oss.str() << std::endl; // prints "A(2)", ok"}
{// possible with C++11, because of the rvalue overload
std::cout << (std::ostringstream() << a).str() << std::endl; //prints "A(2)", ok
}
}
Как вы можете видеть в C ++ 11, мы можем достичь того, чего не можем добиться в C ++ 98. То есть использовать ostringstream
(или же ofstream
) на месте. Теперь возникает вопрос OP, две перегрузки выглядят очень похоже, можно ли объединить обе?
Одним из вариантов является использование универсальной ссылки (Ostream&&
) и опционально с enable_if
ограничить тип. Не очень элегантно
Используя этот пример «реального мира», я обнаружил, что если вы хотите использовать один и тот же код для lvalue ref и rvalue ref, это потому, что, вероятно, вы можете преобразовать один код в другой!
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
return operator<<(oss, a);
}
Это похоже на бесконечно рекурсивную функцию, но это не потому, что oss
является ссылкой lvalue (да, это ссылка lvalue, потому что у нее есть имя). Так что это вызовет другую перегрузку.
Вам все еще нужно написать две функции, но у одной есть код, который вам не нужно поддерживать.
Таким образом, если «имеет смысл» © применять функцию как к (неконстантной) lvalue-ссылке, так и к rvalue, это также означает, что вы можете преобразовать r-значение в l-значение и, следовательно, вы перенаправляете в одну функцию. Обратите внимание, что «это имеет смысл» зависит от контекста и значения предполагаемого кода, и это то, что мы должны «сказать» компилятору, явно вызвав перегрузку lvalue.
Я не говорю, что это лучше, чем использование универсальной ссылки, я говорю, что это альтернатива, и, возможно, цель более ясна.
Редактируемый код здесь: http://ideone.com/XSxsvY. (Обратная связь приветствуется)