LINUX.ORG.RU

Знатокам цпп насчёт вывода типов

 


0

1
template<auto> struct val {};
template<typename... types> struct type: types... {};
template<typename... types> auto func(type<types...>) {}
int main() {
  func(type{val<'\0'>{}, val<0>{}, val<0.>{}});  // работает
  func({val<'\0'>{}, val<0>{}, val<0.>{}});      // не работает
}

Почему оно не смогает? Этому есть какие-то объяснения?

Ответ на: комментарий от BruteForce
std::vector<int> vc0{}; // конструктор без параметров
std::vector<int> vi0{1, 2}; // конструктор с 1 параметром (в векторе [1,2])
std::vector<int> vc1(1, 2); // конструктор с двумя параметрами (в векторе [2], то есть ОДИН элемент)
std::array<int, 2> ac{1,2}; // конструктор с 1 параметром (в массиве [1, 2]), я по глупости эту стрчоку написал))
BruteForce ★★★★
()
Ответ на: комментарий от BruteForce

Одинаковые символы - это наоборот хорошо. Только это не лист, это инициализатор.

template<auto> struct val {};
template<typename... types> struct type: types... {};
auto func(type<val<'\0'>, val<0>, val<0.>>) {}
int main() {
  func({val<'\0'>{}, val<0>{}, val<0.>{}});
}

Здесь уже всё нормально, за лист не посчитал. Да и там типы в любом случае разные, поэтому лист там невозможен.

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

компилятор белым по-русски пишет что не так

Это бессмысленно. С таким же успехом

struct type { ... } var = {...};

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

Как вы этим пользуетесь?

Покажи лучше, будем пользоваться иным.

qweururu
() автор топика

Типы шаблонных параметров не выводятся автоматом никогда, это правило. Во всяком случае так было, что будет дальше - не знаю. Продемонстрировать это можно и без твоего излишне переусложненного кода:

template <typename T>
void f(T) {}

int main() {
	f( 4 );    //ok
	f( {4} ); //error
}

{} - это не экземпляр конкретного типа, это заготовка

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

Только я начинаю думать, что вот вот пойму плюсы и вылезают такие примеры.

Лет до 20 я тоже нет-нет да бывало так подумывал, изредка. Потом чуть более опытный стал и теперь таких мыслей в моей голове не появляется.

Как вы этим пользуетесь?

Как из тюрьмы вышел, так больше и не играю в эту игру.

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

Что значит «невозможен» если шланг это листом считает? В какой-то абстрактной Вселенной-Цепепе?

Здесь уже всё нормально,

«Здесь» компилятор уже и не выводит что значат внешние символы {} в {val<'\0'>{}, val<0>{}, val<0.>{}}, т.к. ты четко указываешь, что это сруктура type

BruteForce ★★★★
()
Ответ на: комментарий от qweururu
struct A { ... } var = {...};

Здесь известно, что = {...} это A{...}. Разница такая же, как между a и b тут:

A a = {1};
auto b = {1}; // initializer_list

link

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

Типы шаблонных параметров не выводятся автоматом никогда, это правило.

type{val<'\0'>{}, val<0>{}, val<0.>{}}
  • написано в хэдпосте. Всё вывелось. Поэтому такого правила нет.

Пример {4} => int некорректен, int это не агрегат, и никакого указания на агрегат в сигнатуре нет. В отличии от моего примера, где оно есть.

@BruteForce:

Что значит «невозможен» если шланг это листом считает?

Значит, что initializer_list содержит одинаковые типы, а в примере они разные. Чем это считает шланг(как и компилятор) я написал выше. «brace-enclosed list» и «initializer_list» это не одно и тоже.

«Здесь» компилятор уже и не выводит что значат внешние символы {} в {val<‘\0’>{}, val<0>{}, val<0.>{}}, т.к. ты четко указываешь, что это сруктура type

func({val<'\0'>{}, val<0>{}, val<0.>{}}); - нет, не указываю. Зачем с никакущим пониманием пытаться лезть?

Здесь известно, что = {…} это A{…}.

В примере известно, что это type. Так же как и в type{...}. Поэтому мимо.

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

написано в хэдпосте. Всё вывелось. Поэтому такого правила нет.

Ты не понимаешь, не пытайся спорить, смотри:

func(type{val<'\0'>{}, val<0>{}, val<0.>{}});

когда ты делаешь этот вызов, то аргумент функции func полностью специализирован. Когда ты делаешь вызов:

func( {val<'\0'>{}, val<0>{}, val<0.>{}} );

функция на вход получает brace-enclosed initializer list (что != std::initializer_list). Из этого не выводятся и никогда не выводились шаблонные параметры.

Пример {4} => int некорректен, int это не агрегат, и никакого указания на агрегат в сигнатуре нет. В отличии от моего примера, где оно есть.

Вообще неважно. Ты думаешь, что из-за твоей «подсказки» компилятору в виде type<…> оно чего-то там тебе должно вывести. Повторяю, это так не работает и не работало никогда, это правило

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

Значит, что initializer_list содержит одинаковые типы, а в примере они разные.

Ты прав, я не прав! Это не std::initializer_list, это initializer-list.

func({val<‘\0’>{}, val<0>{}, val<0.>{}}); - нет, не указываю. Зачем с никакущим пониманием пытаться лезть?

Нет, указываешь

auto func(type<val<‘\0’>, val<0>, val<0.>>) {}

Зачем с никакущим пониманием пытаться лезть?

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

когда ты делаешь этот вызов, то аргумент функции func полностью специализирован

Я приводил этот

template<auto> struct val {};
template<typename... types> struct type: types... {};
auto func(type<val<'\0'>, val<0>, val<0.>>) {}
int main() {
  func({val<'\0'>{}, val<0>{}, val<0.>{}});
}

пример, и там тип нигде полностью не специализирован. Также я приводил пример в хэдпосте, где написано type{...} - так же никакой «полной специализации типа», но всё работает. Поэтому также мимо.

Из этого не выводятся и никогда не выводились шаблонные параметры.

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

Ты думаешь, что из-за твоей «подсказки» компилятору в виде type<…> оно чего-то там тебе должно вывести.

Да, думаю должен. Контекстность - это основа вывода типов(и вывода чего угодно ещё).

Повторяю, это так не работает и не работало никогда, это правило

У правила должно быть обоснование, иначе это не правило, а догмат. Его я и пытаюсь узнать, мне оно не очевидно.

@BruteForce:

Нет, указываешь

Полный код, если так сложно для тебя:

template<auto> struct val {};
template<typename... types> struct type: types... {};
auto func(type<val<'\0'>, val<0>, val<0.>>) {}
int main() {
  func({val<'\0'>{}, val<0>{}, val<0.>{}});
}

Никакого указания.

auto func(type<val<‘\0’>, val<0>, val<0.>>) {}

Указание уровня type<что-то>есть в сигнатуре. Не спами мне пж своим непониманием.

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

Я думаю, что писать на брейнфаке можно на любом языке. Просто не надо так делать.

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

Указание уровня type<что-то>есть в сигнатуре.

Не type<что-то>, а type<val<'\0'>, val<0>, val<0.>> и это законченый тип не требующий от компилятора вывода.

А здесь

template<typename... types> auto func(type<types...>) {}

компилятор должен вывести types.

страница 436, Deducing template arguments from a function call:

… an initializer list argument causes the parameter to be considered a non-deduced context

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

Только я начинаю думать, что вот вот пойму плюсы и вылезают такие примеры.

Ты попробуй в стандартные либы загляни.

Лично я для себя решил: я скажу что я понял С++ когда смогу спокойно без напряга читать стандартные инклуды.

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

А ты форматирование соблюдай, а не пиши в одну строчку.

И вообще посмотри как «страшные» шаблоны в бусте пишут.

Пиши хотя бы так:

<template
      class T1
    , class T2
    , class T3
    , class ...ArgList
>
struct TemplateName
{
    // code here
}

zx_gamer ★★★
()

В общем, единственное более-менее адекватное объяснение - скорость компиляции. Наподобие этого:

auto f(int) {}
namespace {
auto f() {}
auto g() { f(0); }  // не смогает, и также без понятных мне причин
}

Потому как взять шаблоны типов из сигнатур намного сложнее, чем в ситуации с ctad, когда шаблон один и он уже написан. Хотя вне шаблонов проблем нет:

template<auto> struct val {};
template<typename... types> struct type: types... {};
auto func(int) {}
auto func(type<val<'\0'>, val<0>, val<0.>>) {}
int main() {
  func({val<'\0'>{}, val<0>{}, val<0.>{}});
}

– тут оно смогло и всё работает. Придётся пока(снова) цпп отложить и пилить как прежде на ц. Просто чтобы разочарования было на порядок меньше.

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

Я приводил этот

У тебя здесь func() не шаблонный, это совсем другой кейс. Я ведь тебе писал про «шаблонные параметры не выводятся автоматом».

Вопрос почему не выводятся? В других местах компилятор может и использует контекст для вывода, а тут почему-то нет.

Тебе, видимо, не ехать, а шашечки нужны. Я пас. Проблемы здесь нет никакой, можно заюзать фабричный метод, например

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

У тебя здесь func() не шаблонный, это совсем другой кейс.

Оно берёт контекст из сигнатуры(и из него понимает, что значат скобочки), понимать это в случае шаблонов - вполне логичное продолжение такого подхода. Поэтому кейс другой технически, но концептуально он тот же.

Тебе, видимо, не ехать, а шашечки нужны.

Ну мне нужно понимать причины наблюдаемого. Слишком надоело многокопипасты в ц-коде, поэтому я решил попробовать на цпп. Но в цпп вылезает много нюансов, на которые тратится много времени/сил, и это для меня ещё хуже копипасты.

Я пас.

Ок.

Проблемы здесь нет никакой, можно заюзать фабричный метод, например

(tuple<types1...>, tuple<types2...>, остальное...) - настоящая сигнатура, иначе мне эти type/tuple/иная группировка паков не нужны были бы. Разбивать на несколько вызовов не хочу - некрасиво/неудобно получится и половина смысла цпп потеряется. Подожду ещё, может допилят когда-нибудь.

qweururu
() автор топика

В C++ очень простая выводилка типов - взять тип результата справа и принять тип слева равным ему (с некоторыми нюансами для обработки ссылок и const).

Это не Rust, где компилятор решает систему уравнений, чтобы найти известные типы.

Впрочем, в плюсах с их темплейтами, возможно, это и нерешаемая задача. Слишком много возможных вариантов и слишком неинтуитивный результат может получиться.

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

Ну мне нужно понимать причины наблюдаемого.

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

Заставить работать твою хотелку совсем не сложно:

#include <utility>

template<auto> struct val {};
template<typename... types> struct type: types... {};
template<typename... types> auto func(types &&...t) {
	type<types...> var{std::forward<types>(t)...};
}
int main() {
  func(type{val<'\0'>{}, val<0>{}, val<0.>{}});  // работает
  func(val<'\0'>{}, val<0>{}, val<0.>{});      // работает
}

При необходимости, можно концептами включить/отключить какие-нибудь шаблоны

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

не смогает, и также без понятных мне причин

Чтобы не было хрен пойми какого поведения из-за мешанины перегрузок из разных неймспейсов. Очевидно же.

Ну и по ОП-посту:

Этому есть какие-то объяснения?

У тебя рекурсивная зависимость между выводом типа функции и выводом аргумента функции - чтобы инстанцировать шаблонную функцию, надо знать типы аргументов, а чтобы конвертировать brace-enclosed initializer list в конкретный тип, надо знать тип функции, включающий в сигнатуру типы аргументов.

r--r--r--
()
Последнее исправление: r--r--r-- (всего исправлений: 4)
Ответ на: комментарий от r--r--r--

Чтобы не было хрен пойми какого поведения из-за мешанины перегрузок из разных неймспейсов.

А это пример гениальных рассуждений, по причине которых в цпп много чего не работает. Примерно уровня растошлюхи, предлагаемой выше. «Проблема с шаблонами? - А вот в растошлюхе таких проблем нет». Шаблонов правда тоже нет, но это типичный их путь.

У тебя рекурсивная зависимость между выводом типа функции и выводом аргумента функции - чтобы инстанцировать шаблонную функцию, надо знать типы аргументов, а чтобы конвертировать brace-enclosed initializer list в конкретный тип, надо знать тип функции, включающий в сигнатуру типы аргументов.

И снова гениальность с попытками объяснять реальность какими-то простейшими моделями из детсада. Причём пример на эту тему я показывал выше, да и он очевиден тем, кто работает с цпп.

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

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

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

А это пример гениальных рассуждений, по причине которых в цпп много чего не работает.

Продемонстрируй пример своих гениальных рассуждений, как это должно работать в твоём манямирке:

void f(int){}
namespace
{
    void f(long){}
    void ff(){f('\0');}
}

void main()
{
    ff();
}

а мы посмеёмся.

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

В этом одна из проблем правил - попавшим под влияние и принявшим эти правила очень тяжело выйти за их(правил) рамки. Все рассуждения сводятся к «как можно отказываться, это же правила!». Что-то новое сразу вызывает ступор.

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

Кстати, именно это ограничение убирается локально и далее всё(локально) работает как нужно.

auto f(int) {}
namespace {
using ::f;
auto f(long) {}
auto g() { f(0.); } // неоднозначность, как и должно быть
}

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

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

именно это ограничение убирается локально

А ты точно знаком с c++? Ты никуда не убрал никакое ограничение, а сменил неймспес.

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

Ты свою маняверсию как это должно было бы работать изложешь или нет?

попавшим под влияние и принявшим эти правила очень тяжело выйти за их(правил) рамки.

Давай, будь мужыком, выйди за рамки и расскажи нам, как надо было сделать.

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

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

по поводу {} — это инициализатор — а вот какой тип уже инициализировать в шаблоне функции — компилятор понять не может.

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

лично на мой взгляд лучше использовать https://eel.is/c draft/ — тут конкретно можно выделить положение, которое нужно, а также это является на сколько я понимаю оффициальным рабочим драфтом от коммитета по разработке стандарта С++, где одни из последних изменений присутствуют.

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

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

как думается им не долго осталось — будут модули онли...

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

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

anonymous
()
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария