Удалить ссылку в decltype (вернуть T вместо T &, где T & является объявлением)
(Если вы являетесь разработчиком С++ 11, перейдите к полужирным абзацам.)
Скажем, я хочу написать метод шаблона, который вызывает и возвращает результат переданного объекта, тип которого является параметром шаблона:
template<ReturnType, T>
ReturnType doSomething(const T & foo) {
return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}
Итак, T
должен иметь метод ReturnType T::bar() const
для использования в вызове типа:
struct MyClass {
...
int bar() const;
...
};
...
MyClass object;
int x = doSomething<int, MyClass>(object);
Нам не нужно писать MyClass
благодаря вычету типа, и вызов становится:
int x = doSomething<int>(object);
Но исключение <int>
также приводит к ошибке компиляции, потому что метод не требует возврата int для назначения ему x
(он мог бы возвращать char
, например).
В С++ 0x/11 мы имеем auto
и decltype
, с помощью которых мы можем использовать вывод типа метода шаблона:
template<T>
auto doSomething(const T & foo) -> decltype(foo.bar()) {
return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}
Теперь компилятор узнает, что такое тип foo.bar()
, и просто использует это как возвращаемый тип. С нашим конкретным классом MyClass
это будет int
, и было бы достаточно:
int x = doSomething(object);
Теперь на мой вопрос:
Если MyClass определяет bar()
как возвращающий int&
, возвращаемый тип doSomething(object)
также будет int&
= decltype(foo.bar())
. Это проблема, поскольку, поскольку g++ теперь соответствует тому, что я возвращаю ссылку на временную.
Как я могу это исправить? Есть ли что-то вроде remove_reference
, которое можно использовать как remove_reference(decltype(foo.bar()))
?
Я думал о просто объявлении вспомогательного метода, который принимает T&
и возвращает a T
, а затем определяет тип возврата doSomething
как decltype(helper(foo.bar()))
. Но должен быть лучший способ, я чувствую это.
Ответы
Ответ 1
Чтобы удалить ссылку:
#include <type_traits>
static_assert(std::is_same<int, std::remove_reference<int&>::type>::value, "wat");
В вашем случае:
template <typename T>
auto doSomething(const T& foo)
-> typename std::remove_reference<decltype(foo.bar())>::type
{
return foo.bar();
}
Чтобы быть понятным, обратите внимание, что как написано, возвращающее ссылку просто отлично:
#include <type_traits>
struct f
{
int& bar() const
{
static int i = 0;
return i;
}
};
template <typename T>
auto doSomething(const T& foo)
-> decltype(foo.bar())
{
return foo.bar();
}
int main()
{
f x;
return doSomething(x);
}
Возвращенная ссылка может быть просто передана без ошибок. Ваш пример в комментарии, где он становится важным и полезным:
template <typename T>
auto doSomething(const T& foo)
-> decltype(foo.bar())
{
return foo.bar() + 1; // oops
}