c++ template deduction by requested returned type

Fihtangolz
Fihtangolz

Вывод типа от запрашиваемого возврашаемого типа. 

template<typename T>

T matrix_allocate(int height, int width){

//some

}

-3
рейтинг
7 комментариев
Andrey Davydov

Для того чтобы это заработало должна существовать некоторая концепция expected type, а ее в C++ нет. Наверное, писав это преждложение Вы имели в виду, что-то вроде 

Marix m = matrix_allocate(1024, 1024);

но в этом случае ничем не хуже использовать 

auto m = matrix_allocate<Matrix>(1024, 1024);

В более же общей ситуации, когда вызов функции matrix_allocate является частью другого выражения, скажем foo(matrix_allocate(1024, 1024)), неизвестно аргумент какого типа ожидает foo (потому что перегрузок foo может быть много), более того мы даже не можем все перегрузки foo, не зная типы аргументов (ADL).

Andrey Davydov
Fihtangolz

Andrey Davydov, для foo мне кажется стоит использовать принци "единственной точки зависимости" тоесть в данном случае требование должно быть разрешимо единственным образом и зависет от одной точки требования. Вообшем если мы запроси шаблонный параметр в качестве типа мы должны получит туже ошибку что сейчас и явно передать тип как шаблонный параметр если же функция предположим имеет статический тип int все хорошо или хорошо если Matrix b = foo(matrix_allocate(1024, 1024)); где T foo(T __s){ //... }. Ну вопрос тут скорей в следуюшем, почему бу и нет если это никому хуже не сделает? А писать так чаще удобней чес с auto

Fihtangolz
webreh

Andrey Davydov, ну вообще не особо нужна, результат достижим. Другое дело, что, видимо, нужно явно помечать "template <return typename T>", иначе компилятору может быть не очень понятно, что надо действовать как ниже

#include <tuple>
#include <utility>

namespace details {
    template <typename Tuple>
    struct return_type_deductor { 
        static_assert(!std::is_reference_v<Tuple>);
        Tuple tuple_;

        return_type_deductor(Tuple&& tuple) : tuple_(std::forward<Tuple>(tuple)) {
        }

        template <typename T> struct tag {};

        template <typename T> operator T() && {
            return std::move(*this).details(tag<T>{}, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
        }

        template <typename T, std::size_t... I>
        T details(tag<T>, std::index_sequence<I...>) && {
            return T{std::get<I>(std::move(tuple_))...};
        }
    };
}

template <typename... Args>
auto deduce_by_return_type(Args&&... args) {
    return details::return_type_deductor{std::make_tuple<Args...>(std::forward<Args>(args)...)};
}

auto matrix_allocate(int height, int width) { 
    return deduce_by_return_type(height, width); 
}

template <typename T>
T matrix_allocate(int height, int width) { 
    return deduce_by_return_type(height, width); 
}

struct matrix {
    int height, width;
};

void foo() {
    matrix a = matrix_allocate(12, 12);
    auto b = matrix_allocate<matrix>(12, 12);
}

 

webreh
webreh

Andrey Davydov, и да, ваш пример с auto немедленно ломается, когда deduction происходит по параметру функции.

matrix transpose(matrix x) {
    return { x.width, x.height } ;
}

void goo() {
    auto c = transpose(matrix_allocate(10, 12));
}
webreh
Fihtangolz

Andrey Davydov, вообщем константин красавец, реализация как сахар на cast вполне себе вкусная, он поскромничал дописать, что можно так:

template <typename T = deduce_by_return_type_functor>

T matrix_allocate(int height, int width) { 

    return T{height, width}; 

}

, вообщем ожидается что в стандарт попадет вот это вот как стандартное поведение для типа возврата, если не указанное любое другое значение по умолчанию 

Fihtangolz
Обновлено 
Andrey Davydov

webreh, то что Вы написали безусловно красивый трюк, хотя он и описывается двумя стандартными функциями std::forward_as_tuple, std::make_from_tuple (по модулю того, что там используется инициализация круглыми скобками, а у Вас фигурными, но неизвестно, что именно нужно пользователю), но при чем здесь вывод типа? Так, способ вернуть proxy. А если allocate состоит не из одного return statement, а должен сделать еще что-то полезное с возвращаемым значение (заполнить нулями, скажем)? 

T allocate(int width, int height) {
  T result = ...;// construct somehow
  result.fill(0);
  return result;
}

Что до примера с transpose, то он является яркой иллюстрацией того о чем я писал в первом сообщении -- для того, чтобы так называемый вывод типа работал нужно знать, какой тип ожидается. Если перегрузка transpose одна -- работает, если добавить еще 

struct square_matrix {};

square_matrix transpose(square_matrix x) {
    return {};
}

работать не будет.

Andrey Davydov
webreh

Andrey Davydov, сам чуть не написал через forward_as_tuple, но так нельзя, нельзя forward в выходящую за область функции view структуру.

Трюк прямо демонстрирует возможный (для компилятора) вывод шаблонного параметра по результирующему значению - вести себя как он бы вел себя в этом примере.

По поводу перегрузок - да, не будет (хотя для square_matrix будет если добавить там SFINAE условие - оно же не конструируется по двум параметрам) работать для параметров, которые фактически участвуют в разрешении перегрузки, что логично.

Конструкция типа выше может использоваться для параметров, которые не участвуют в разрешении перегрузки, скажем вы сможете не указывать явно шаблонный параметр boost::lexical_cast в случае vector<std::string>::push_back

webreh
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения