(Этот ответ такой же, как у @UncleBens, но немного более общий, так как он идеально передает любые аргументы.)
Это очень полезно в таких языках, как haskell, где, например, read
будет принимать строку в качестве входных данных и будет анализировать ее в соответствии с требуемым типом возврата.
(Вот пример кода на ideone .)
Сначала начнем с функции foo
, тип возвращаемого значения которой мы хотим вывести:
template<typename Ret>
Ret foo(const char *,int);
template<>
std::string foo<std::string>(const char *s,int) { return s; }
template<>
int foo<int >(const char *,int i) { return i; }
Когда запрашивается строка, он возвращает строку, которая находится в первом аргументе. Когда его спросят о int, он вернет второй аргумент.
Мы можем определить функцию auto_foo
, которую можно использовать следующим образом:
int main() {
std::string s = auto_foo("hi",5); std::cout << s << std::endl;
int i = auto_foo("hi",5); std::cout << i << std::endl;
}
Чтобы это работало, нам нужен объект, который временно сохранит аргументы функции, а также запустит функцию, когда ее попросят преобразовать в нужный тип возвращаемого значения:
#include<tuple>
template<size_t num_args, typename ...T>
class Foo;
template<typename ...T>
class Foo<2,T...> : public std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this)); }
};
template<typename ...T>
class Foo<3,T...> : std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this), std::get<2>(*this)); }
};
template<typename ...T>
auto
auto_foo(T&&... args)
// -> Foo<T&&...> // old, incorrect, code
-> Foo< sizeof...(T), T&&...> // to count the arguments
{
return {std::forward<T>(args)...};
}
Кроме того, вышеприведенное работает для функций с двумя или тремя аргументами, нетрудно понять, как их расширить.
Это много кода для написания! Для каждой функции, к которой вы хотите применить это, вы можете написать макрос, который сделает это за вас. Примерно так в верхней части вашего файла:
REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
// necessary structure and auto_???
и тогда вы можете использовать auto_foo
в вашей программе.