LINUX.ORG.RU

c++ function return type deduction

 


0

2

Что не так с reallyAsync4? Почему она не компилируется? Я понимаю, что reallyAsync3 и reallyAsync4 не будут работать с указателями на членов класса, как объясняется здесь: https://stackoverflow.com/questions/15673792/whats-the-difference-between-res..., но меня интересует просто синтаксис.. Где в reallyAsync4 ошибка и как ее написать правильно без использования result_of?

#include <iostream>
#include <future>
using namespace std;
using namespace literals;

template<typename F, typename... Ts>
future<typename result_of<F(Ts...)>::type>
reallyAsync(F&& f, Ts&&... params)
{
    return async(launch::async,
                 forward<F>(f),
                 forward<Ts>(params)...);
}

template<typename F, typename... Ts>
auto reallyAsync2(F&& f, Ts&&... params)
    -> future<typename result_of<F(Ts...)>::type>
{
    return async(launch::async,
                 forward<F>(f),
                 forward<Ts>(params)...);
}

template<typename F, typename... Ts>
auto reallyAsync3(F&& f, Ts&&... params)
    -> future<decltype(forward<F>(f)(forward<Ts>(params)...))>
{
    return async(launch::async,
                 forward<F>(f),
                 forward<Ts>(params)...);
}

/*
template<typename F, typename... Ts>
typename future<decltype(declval<F>()(declval<Ts>()...))>
reallyAsync4(F&& f, Ts&&... params)
{
    return async(launch::async,
                 forward<F>(f),
                 forward<Ts>(params)...);
}
*/

template<typename F, typename... Ts>
auto reallyAsync5(F&& f, Ts&&... params)
{
    return async(launch::async,
                 forward<F>(f),
                 forward<Ts>(params)...);
}

int main(int argc, char *argv[])
{
    auto fut = reallyAsync5([]()
        {
            while (true)
            {
                cout << "In thread" << endl;
                this_thread::sleep_for(1s);
            }
        });
    return 0; // won't happen
}
★★

Последнее исправление: dissident (всего исправлений: 4)

Вот так у меня всё работает:

#include <iostream>
#include <future>
using namespace std;
using namespace literals;

template<typename F, typename... Ts>
future<typename result_of<F(Ts...)>::type>
reallyAsync(F&& f, Ts&&... params)
{
	return async(launch::async,
				 forward<F>(f),
				 forward<Ts>(params)...);
}

template<typename F, typename... Ts>
auto reallyAsync2(F&& f, Ts&&... params)
	-> future<typename result_of<F(Ts...)>::type>
{
	return async(launch::async,
				 forward<F>(f),
				 forward<Ts>(params)...);
}

template<typename F, typename... Ts>
auto reallyAsync3(F&& f, Ts&&... params)
	-> future<decltype(forward<F>(f)(forward<Ts>(params)...))>
{
	return async(launch::async,
				 forward<F>(f),
				 forward<Ts>(params)...);
}


template<typename F, typename... Ts>
auto reallyAsync4(F&& f, Ts&&... params) -> future<decltype(declval<F>()(declval<Ts>()...))>
{
	return async(launch::async,
				 forward<F>(f),
				 forward<Ts>(params)...);
}


template<typename F, typename... Ts>
decltype(auto)
reallyAsync5(F&& f, Ts&&... params)
{
	return async(launch::async,
				 forward<F>(f),
				 forward<Ts>(params)...);
}

int main(int argc, char *argv[])
{
	auto fut = reallyAsync4([]()
		{
			while (true) {
				cout << "In thread" << endl;
				this_thread::sleep_for(1s);
			}
		});
	fut.wait_for(10s);
	return 0;
}

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Хмм, весь смысл в declval'ах был в том, чтобы избавиться от -> и перенести тип «налево» с typename. Почему это не получается?

dissident ★★
() автор топика
Ответ на: комментарий от fsb4000

Собралось без typename, спасибо:

template<typename F, typename... Ts>
future<decltype(declval<F>()(declval<Ts>()...))>
reallyAsync4(F&& f, Ts&&... params)
{
    return async(launch::async,
                 forward<F>(f),
                 forward<Ts>(params)...);
}

А объясните тогда пожалуйста, когда его нужно использовать? Когда в выражении тип шаблона non-deducible? Типо typename X<T>::type? К X<decltype<T>>::type это не относится?

dissident ★★
() автор топика
Ответ на: комментарий от dissident

Вообще я просто загуглил ошибку компиляции: «expected nested-name-specifier» и понял что она от лишнего typename

«expected nested-name-specifier» means that after typename keyword you are expected to use some nested name of a template parameter

fsb4000 ★★★★★
()
Ответ на: комментарий от dissident

А объясните тогда пожалуйста, когда его нужно использовать?

Когда не ясно, что некая синтаксическая конструкция — это имя типа.

Ваш К.О.

utf8nowhere ★★★
()

Не юзай auto в качестве return type. Не иди на поводу у придурков, которые протащили это в стандарт.

1) апи не должен зависеть от неализации

2) читабельность объявления auto f(T1, T2) нулевая - нужно лезть в имплементацию, чтобы понять что там будет возвращаться.

В общем раньше компилятор выводил типы, а сейчас эти ловцы новизны перекладывают это на программиста и на пользователя API.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от eao197

Я никому ничего не запрещаю, кроме случаев, когда делаю code review.

А эта хрень с возвращением auto уже пошла в массы. Ещё сложнее будет понять как использовать этот код. А с учётом неявных приведений типов (типа char * -> std::string) ещё веселее будет.

А возвращать лямбды можно через decltype, если вдруг такое понадобится раз в триста лет.

Да и не вижу я чтобы топикстартер возращал лямбды.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 5)
Ответ на: комментарий от dzidzitop

А возвращать лямбды можно через decltype, если вдруг такое понадобится раз в триста лет.

Как? Ведь каждая лямбда это отдельный класс. Т.е.:

decltype([]() { return 0; }) != decltype([]() { return 0; })

dissident ★★
() автор топика
Ответ на: комментарий от dzidzitop

Не юзай auto в качестве return type.
Я никому ничего не запрещаю,

о'кай

А возвращать лямбды можно через decltype, если вдруг такое понадобится раз в триста лет.

Это как?

eao197 ★★★★★
()
Ответ на: комментарий от dissident

decltype([]() { return 0; }) != decltype([]() { return 0; })

Все правильно, то он тупит, хотя если не нужен захват переменных, то:

decltype(+[]() { return 0; }) == decltype(+[]() { return 0; })

Ну или можно явно выдрать operator(), который в таком случае будет static.

anonymous
()

Наоборот, используй auto в качестве return type, во всех функциях.

  • Во-первых, это унификация лямбд и функций.
  • Во-вторых, это расширяет возможности языка. Вот смотри, с помощью auto и -> можно упростить многие шаблоны, потому что теперь для decltype доступны параметры функции, а не только типы.
#include <iostream>

template<typename T1, typename T2>
auto sum(T1 a, T2 b) -> decltype(a+b)
{
    return (a + b);
}

/*
// error: ‘a’ was not declared in this scope
// error: ‘b’ was not declared in this scope

template<typename T1, typename T2>
decltype(a+b) sum2(T1 a, T2 b)
{
    return (a+b);
}
*/

auto main() -> int
{
    std::cout << sum(1,2) << std::endl;
//    std::cout << sum2(1,2) << std::endl;
}

Главное, когда мы знаем какой тип будет возвращен, то указывать его после стрелочки. У лямбд тоже можно, и нужно для лучшей читаемости указывать тип после стрелки...

#include <iostream>

auto main() -> int
{
    auto sum = [](double a, double b) -> double
    {
        return a + b;
    };
    std::cout << sum(1.0,2.0) << std::endl;
}

Не слушай кота, в комитете isocpp знают С++ лучше форумного кота, и лучше пользоваться всеми предоставляемыми возможностями современного С++

fsb4000 ★★★★★
()
Ответ на: комментарий от dissident

объявить лямбду как внешний тип, если без захвата переменных.

using x = []() { return 0; };

или

auto y = []() { return 0; };
decltype(y);


а если нужен захват переменных, то возвращать функтор.

dzidzitop ★★
()
Ответ на: комментарий от fsb4000

тов. майор,

1) auto f() -> double это совсем не auto f();
пользователю не нужно догадываться, что за тип возвращает функция, и тип возвращаемого значения объявлен явно.

2) нахрен оно не нужно в большинстве случаев, даже если код мегашаблонный.

3) пользоваться всеми предоставляемыми возможностями - это почти как пользоваться всеми паттернами проектирования.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 2)
Ответ на: комментарий от fsb4000

Не слушай кота, в комитете isocpp знают С++ лучше

Какое отношение рандомный форумный нонейм с помойки имеет к isocpp? При этом рандом несёт явную ахинею.

template<typename T1, typename T2>
auto sum(T1 a, T2 b) -> decltype(a+b)
{
    return (a + b);
}

Эта параша попросту не имеет смысла, ведь никакой decltype тут не нужен.

Стрелка - это кастыль и не более и нужен этот кастыль почти никогда, а в твоём кейсе - не нужен вообще.

Я вот не понимаю вас, малоразвитых идиотов, вот ты выклал «decltype(a+b)», ты мало того, что сделал то же самое, что делает return (a + b); и что попросту не имеет смысла, дак к тому же, как ты, идиот, это будешь писать, если там будет что-то большее, нежели один оператор? Будешь тело функции пастить в деклтайп? Что-бы пердануть в лужу?

Хотя тут сразу можно делать вывод: пишешь скобочки как идиот - ты идиот. Всё просто.

Главное, когда мы знаем какой тип будет возвращен, то указывать его после стрелочки.

Достаточно тебе, малоразвитое чудовище, задать простой вопрос - зачем? И ты тут же сядешь в лужу. Ведь твои потуги попросту не имеют смысла.

anonymous
()
Ответ на: комментарий от anonymous

Гы, столько лет тут не был, а ничего не поменялось. Зачем так сраться из-за всего лишь языка программирования.

По поводу don't use auto (а don't use auto return types - это подмножество холивара don't use auto at all, по крайней мере я такое слышал от одного молодого преподавателя), это же уже тот же Майерс объяснял, например тут:

https://youtu.be/J-tA17slViE?t=413

С примерами:

map<string,int> m;
for (std::pair<std::string, int>& p : wordCount) {
    // создает копию пары
}

так как надо было (или с auto):

for (const std::pair<const std::string, int>& p : wordCount) {
    // не создает
}

Или вот:

auto f = [](){...} // не создает копии, занимает меньше места
function<void(void)> f = [](){...} // наоборот

В линке на youtube больше примеров.

А decltype после стрелочки был нужен в C++11 где нельзя было использовать auto return type. Использовать такой или другой синтаксис - это как я понимаю дело вкуса, coding guidance'а или религиозный убеждений (тип можно посмотреть в IDE, например).

И так далее. Т.е. большинство решило за auto, так что холивор не имеет смысла. Просто теперь такой язык. Меня интересовал синтаксис и ответы я получил, спасибо!

dissident ★★
() автор топика
Последнее исправление: dissident (всего исправлений: 2)
Ответ на: комментарий от dissident

А decltype после стрелочки был нужен в C++11

Он и сейчас нужен, но в очень редком случае, если это шаблонная функция, в которой есть два return с разными типами, и на основании параметров можно вывести тип результата функции.

// другой анонимус

anonymous
()
Ответ на: комментарий от anonymous

А можно с примерами про два return'а с разными типами? Просто auto не заработает? Речь о чем-то вроде https://stackoverflow.com/questions/32885112/template-function-to-return-tupl... ?

Убрал decltype, в C++14 продолжает работать:

#include <iostream>
#include <string>
#include <tuple>

// IDs
enum class ID : size_t
{
    AAA, // 0
    BBB, // 1
    CCC, // 2
    DDD  // 3
};

// default values for each ID
const auto defaultValsForIDs = std::make_tuple(
        int(1),             // 0
        std::string("bbb"), // 1 
        double(3.5),        // 2
        int(-5)             // 3
);

template<typename EnumT>
using underlayingEnumT = typename std::underlying_type<EnumT>::type;

template<typename EnumT>
constexpr underlayingEnumT<EnumT> to_underlying(EnumT e) 
{
    return static_cast<underlayingEnumT<EnumT>>(e);
}

template<typename EnumT, EnumT e>
auto getDefaultValue() 
//	-> decltype(std::get<to_underlying<EnumT>(e)>(defaultValsForIDs))
{
    return std::get<to_underlying<EnumT>(e)>(defaultValsForIDs);
}

template<ID id>
auto getDefaultValForID() 
//	-> decltype(getDefaultValue<ID, id>())
{
    return getDefaultValue<ID,id>();
}

int main()
{
    std::cout << getDefaultValForID<ID::BBB>() << std::endl;
    return 0;
}

Или речь о чем-то другом?

dissident ★★
() автор топика
Последнее исправление: dissident (всего исправлений: 3)
Ответ на: комментарий от dissident

Возможно речь о чём-то типа такого. Обычно с контейнерами бывает, или пустую строку когда возвращают могут return {}; писать... Но если return {} это первый return, то тогда auto неправильный тип определит...

#include <iostream>
#include <typeinfo>

/*
//main.cpp: In instantiation of ‘auto f(T1, T2) [with T1 = int; T2 = double]’:
//main.cpp:14:22:   required from here
//main.cpp:8:17: error: returning initializer list
//         return {};
template <typename T1, typename T2>
auto f(T1 a, T2 b)
{
    if (a > b)
        return {};
    return a;
}
*/

template <typename T1, typename T2>
auto f(T1 a, T2 b) -> T1
{
    if (a > b)
        return {};
    return a;
}

auto main() -> int
{
    auto t = f(1, 2.0);
    std::cout << typeid(t).name() << std::endl;
}
fsb4000 ★★★★★
()
Ответ на: комментарий от dissident

По поводу don't use auto (а don't use auto return types - это подмножество холивара don't use auto at all, по крайней мере я такое слышал от одного молодого преподавателя), это же уже тот же Майерс объяснял, например тут:

Зачем ты мне это пишешь?

for (std::pair<std::string, int>& p : wordCount)

Ты хотел написать std::pair<std::string, int> p? Хотя посмотрел - и там было const - ты всё перепутал.

так как надо было (или с auto):

Не так, там тебе же ясно показали, что тип у ключа const string, а const для pair не нужен.

К авто то же не имеет никакого отношения - просто кто-то неправильно написал тип.

function<void(void)> f = [](){...} // наоборот

И опять же - никакого отношения к auto не имеет - это проблемы std::function и того, что ламерки не осилили указатели на функции.

В конечном итоге всё сводится к тому, что stl ущербанское дерьмо, а идиоты не могут правильно наставить типы. И да, auto им поможет их расставить правильно. Но это такой себе плюс auto для людей с мозгом.

Смысл от авто - сокращение( тут идиоты могут спорить) и вывод типов там, где их написать руками невозможно( тут даже совсем отбитый спорить не будет).

А decltype после стрелочки был нужен в C++11 где нельзя было использовать auto return type. Использовать такой или другой синтаксис - это как я понимаю дело вкуса, coding guidance'а или религиозный убеждений (тип можно посмотреть в IDE, например).

Нет никакого вкуса. Есть идиоты и не идиотов, либо как максимум - люди подневольные, которых кто-то заставляет писать так, как хочет тот, кто и заставляет.

И ты либо идиот и пишешь скобочки как идиот, либо нет - и пишешь их нормально. Если ты не идёт и пишешь как идиот - тебя заставили. Естественно, что мало кто в этом признается и приходится себя убеждать в обратном, но это уже к делу отношения не имеет.

И так далее. Т.е. большинство решило за auto, так что холивор не имеет смысла. Просто теперь такой язык. Меня интересовал синтаксис и ответы я получил, спасибо!

Я нигде не говорил про auto - я говорил про стрелочку.

anonymous
()
Ответ на: комментарий от fsb4000

А что мешает объявить как

template <typename T1, typename T2>
T1 f(T1 a, T2 b)

Вроде речь шла о необходимости синтаксиса "->".

dissident ★★
() автор топика
Ответ на: комментарий от dissident

А можно с примерами про два return'а с разными типами?

#include <cmath>
#include <type_traits>
using namespace std;

template<class T, class U>
auto add(T a, U b) -> decltype(a + b) {
    if (is_floating_point_v<T> || is_floating_point_v<U>)
        return floor(a) + floor(b);

    return a + b;
}

int main() {
    add(1, 2);
    add(1.5, 2);
}

Пример ради упрощения очень притянут за уши, плюс тут логично взять if constexpr и тогда тип писать не придется. Но смысл вроде показан. Раз в C++ есть неявные преобразование и конструкторы, то значит иногда придется указывать нужный тип, чтоб понимать что к чему приводить. Не обязательно, кстати, через decltype, чаще это будет параметр шаблона или тип, который через него будет выписан.

anonymous
()
Ответ на: комментарий от fsb4000

Возможно речь о чём-то типа такого.

Не типа, опять ты перданул в лужу с примером, в котором стрелочка нахрен не упала.

И да, кстати, с чего вдруг в рамках твоей шизофрении auto плохо, а {} - не плохо?

Но если return {} это первый return, то тогда auto неправильный тип определит...

Никакого отношения к первому, либо десятому ruturn это не имеет. У {} попросту «неопределённый» тип.

anonymous
()
Ответ на: комментарий от dissident

А что мешает объявить как

Ну вот как раз когда есть выражния, то a + b не всегда имеет тип первого аргумента. int + double -> double, например.

anonymous
()
Ответ на: комментарий от fsb4000

Если что я не тот анонимус-неадекват, что отвечал выше. Пример вполне годный, только лучше было взять не -> T1, а, например, -> common_type_t<T1, T2>

anonymous
()
Ответ на: комментарий от fsb4000

Почему я записал более длинный тип, потому как бывает, что он реально большой, со всякими enable_if_t и пр, и тогда логично либо оставить его отдельной строкой, но это не красиво, либо записать так:

auto foo(...) ->
    enable_if_t<...>
anonymous
()
Ответ на: комментарий от anonymous

Спасибо. Я переделал пример fsb4000 где тоже -> оказывается нужен:

template <typename V, typename V2>
auto f(V v1, V2 v2) -> decltype(v1[0] + v2[0])
{
    if (v1[0] > v2[0])
        return {};
    return v1[0] + v2[0];
}

Правда при желании можно поспорить что в обоих случаях -> можно убрать и заменить на declval:

template<class T, class U>
decltype(declval<T>() + declval<U>()) add(T a, U b) { //-> decltype(a + b) {
    if (is_floating_point<T>::value || is_floating_point<U>::value)
        return floor(a) + floor(b);

    return a + b;
}

Бардак какой-то...

dissident ★★
() автор топика
Ответ на: комментарий от anonymous

Только вот у дошколёнка написано T1 и твоя потуга не имеет смысла.

anonymous
()
Ответ на: комментарий от dissident

Бардак какой-то...

Эти потуги попросту не имею смысла как общий случай - причины просты, что ты будешь делать если у тебя в функции не a + b, а 10 строчек?

Ну и как всегда - тебя обманули. Как все быстро съехали с предложения идиоты везде писать auto -> type, на отдельные, редкие кейсы. При этом ни одного реального кейса так и не последовало.

anonymous
()
Ответ на: комментарий от dissident

Правда при желании можно поспорить что в обоих случаях -> можно убрать и заменить на declval:

Можно, но тут другой анонимус прав - читабельность резко упадет.

anonymous
()
Ответ на: комментарий от anonymous

При этом ни одного реального кейса так и не последовало.

А чем тебе -> с развесистым enable_if_t не кейс? С auto такое будет лучше читаться, чем, если оставить вывод типа перед именем функции.

anonymous
()
Ответ на: комментарий от anonymous

И какое же отношение первые три точки имеют ко вторым?

Используют одинаковые параметры шаблона, например.

anonymous
()
Ответ на: комментарий от anonymous

В каком месте и по какой причине?

anonymous
()
Ответ на: комментарий от anonymous

Используют одинаковые параметры шаблона, например.

Параметры шаблона в аргументах функции?

anonymous
()
Ответ на: комментарий от anonymous

Параметры шаблона в аргументах функции?

В параметрах функции. Аргументы - это то, что передается.

anonymous
()
Ответ на: комментарий от anonymous

А чем тебе -> с развесистым enable_if_t не кейс? С auto такое будет лучше читаться, чем, если оставить вывод типа перед именем функции.

Опять же - никого, кроме табуреток, не интересуют куллстори про читаться. Это первый и последний аргумент идиота. Нет объективных критериев «лучше», а значит всё это - ахинея. Ну и это за кейс никто не выдавал.

Да и вообще, контекст был не о том - я изначально сказал, что для -> кейсы есть и я говорю не о том, что «их нет», а потом, что даже объективный пример с ней хрен придумаешь.

Я не читаю код с телефона, как идиот, я не читаю его с лептопа, как идиот. Меня мало волнует то, сколько там строк занимает сигнатура функции. А если занимает много - я напишу using.

А в каком месте написан enable_if - для меня ни на что не влияет. Только во втором случае(с ->) запись будет длиннее.

anonymous
()
Ответ на: комментарий от anonymous

Меня это мало волнует, ты на вопрос отвечай.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.