is_same_container
От: johny5 Новая Зеландия
Дата: 26.07.24 07:37
Оценка:
Привет всем.

Пытаюсь тут написать std::is_same_container, чтобы понять, является ли тип T какимнть вектором или std::optional.

И вспотыкаюсь с использованием using.
https://godbolt.org/z/reh9a4xK5

using задаёт alias, и вроде когда передаёшь инстанс такого алиаса (using_fun) функции, шаблонные аргументы выводятся как будто я передал оригинальный класс (std::variant<int, long>). И я могу задетектировать сам std::variant, но не могу передавая тип оригинального using-а как template template аргумент, задетектировать что он был использован. Не понимаю почему.

В общем застрял, есть какие то может готовые решения?
Re: is_same_container
От: andrey.desman  
Дата: 26.07.24 15:08
Оценка:
Здравствуйте, johny5, Вы писали:

J>using задаёт alias, и вроде когда передаёшь инстанс такого алиаса (using_fun) функции, шаблонные аргументы выводятся как будто я передал оригинальный класс (std::variant<int, long>). И я могу задетектировать сам std::variant, но не могу передавая тип оригинального using-а как template template аргумент, задетектировать что он был использован.


J>Не понимаю почему.


Сам же выше и ответил. Потому что не вводится новый тип.

J>В общем застрял, есть какие то может готовые решения?


Всякие жуткие костыли вроде https://github.com/joboccara/NamedType

Но я бы не вступал в это.
Re[2]: is_same_container
От: johny5 Новая Зеландия
Дата: 30.07.24 15:42
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Здравствуйте, johny5, Вы писали:


J>>using задаёт alias, и вроде когда передаёшь инстанс такого алиаса (using_fun) функции, шаблонные аргументы выводятся как будто я передал оригинальный класс (std::variant<int, long>). И я могу задетектировать сам std::variant, но не могу передавая тип оригинального using-а как template template аргумент, задетектировать что он был использован.


J>>Не понимаю почему.


AD>Сам же выше и ответил. Потому что не вводится новый тип.


Ну хорошо, не вводится новый тип. Тогда почему когда я передаю using_fun как template template параметр, он не превращается магически в std::variant? С чем конкретно тогда оперирует мой шаблон? Т.е. 46я и 47я строчки должны бы давать одинаковый результат?
Re[3]: is_same_container
От: andrey.desman  
Дата: 30.07.24 19:49
Оценка: +1
Здравствуйте, johny5, Вы писали:

J>Ну хорошо, не вводится новый тип. Тогда почему когда я передаю using_fun как template template параметр, он не превращается магически в std::variant? С чем конкретно тогда оперирует мой шаблон? Т.е. 46я и 47я строчки должны бы давать одинаковый результат?


Невнимательно прочитал вопрос.

An alias template name is never deduced.

То есть, использовать имя шаблонного алиаса таким образом не получится, оно не выведется из аргумента. Надо либо указывать базовый шаблон или сравнивать уже полный тип is_same_v<using_fun<long>, decltype(d)>.
Re: is_same_container
От: Кодт Россия  
Дата: 14.08.24 10:09
Оценка: 9 (1)
Здравствуйте, johny5, Вы писали:


J>using задаёт alias, и вроде когда передаёшь инстанс такого алиаса (using_fun) функции, шаблонные аргументы выводятся как будто я передал оригинальный класс (std::variant<int, long>). И я могу задетектировать сам std::variant, но не могу передавая тип оригинального using-а как template template аргумент, задетектировать что он был использован. Не понимаю почему.


Можно упростить пример — чтобы он не ложно-отрицательный ответ давал на SFINAE, а тупо не компилировался

https://godbolt.org/z/4djaYTWxd

#include <variant>
#include <iostream>

template<typename T>
using using_fun = std::variant<int, T>;

template<template<typename...> class TEMPLATE, class... PARAMS>
void check(TEMPLATE<PARAMS...>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }

template<template<typename...> class TEMPLATE, class... PARAMS>
void nocheck(auto) { std::cout << __PRETTY_FUNCTION__ << std::endl; }

template<template<typename> class TEMPLATE, class PARAM>
void check1(TEMPLATE<PARAM>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }


int main() {
    std::variant<int, long> a;

    check(a);
    check<std::variant>(a);

    nocheck<std::variant, int, long>(a);

    check<std::variant, int>(a);  // long довывелся
    check<std::variant, int, long>(a);
    //check<using_fun>(a);  // как вывести T для using_fun<T> из variant<int,long> ?

    //check1(a);  // ну, тут понятно: ждём шаблон с 1 параметром, а подсунули с двумя
    check1<using_fun>(a);
    check1<using_fun, long>(a);
}


Мне кажется, что компилятор ещё и упирается в двойное неявное преобразование / вывод параметров. Двойное — запрещено.
(Криво объяснил, знаю...)
Перекуём баги на фичи!
Re[2]: is_same_container
От: johny5 Новая Зеландия
Дата: 15.08.24 02:39
Оценка:
К>int main() {
К>    std::variant<int, long> a;

К>    check(a);
К>    check<std::variant>(a);

К>    nocheck<std::variant, int, long>(a);

К>    check<std::variant, int>(a);  // long довывелся
К>    check<std::variant, int, long>(a);
К>    //check<using_fun>(a);  // как вывести T для using_fun<T> из variant<int,long> ?

К>    //check1(a);  // ну, тут понятно: ждём шаблон с 1 параметром, а подсунули с двумя
К>    check1<using_fun>(a);
К>    check1<using_fun, long>(a);
К>}
К>


Если ещё добавить с вектором
int main() {
    using_fun<long> a;

    check<std::variant>(a);
    check<using_fun>(a);  // как вывести T для using_fun<T> из variant<int,long> ?
    check<std::vector>(a);  //  тоже ломается :()
}


И в целом различить вектор от using_fun получается невозможным.

Если компайлер заставить распечатать тип:
template<template<typename...> class TEMPLATE>
void print() { std::cout << __PRETTY_FUNCTION__ << "\n"; }

int main() {
    print<using_fun>();
}


void print() [with TEMPLATE = using_fun]


Т.е. using_fun магически превращается в тип, который, при этом, никак нельзя decay-ить в std::variant. Это что то ad-hoc, не думаю что это оговорено в стандарте.
Re[3]: is_same_container
От: vopl Россия  
Дата: 15.08.24 11:34
Оценка: 6 (1)
Здравствуйте, johny5, Вы писали:

J>
J>int main() {
J>    using_fun<long> a;

J>    check<using_fun>(a);  // как вывести T для using_fun<T> из variant<int,long> ?
J>}
J>



Из ошибкок компилятора можно понять что он спотыкается при попытке сведЕния пака и штучного параметра. Вероятно, это какая то тонкая недоработка языка, потому что в других ситуациях такое сведение делается замечательно. Ближе к практике. Если обеспечить наличие перегрузки check, в которой не надо будет сводить пак со штучным параметром, то все получается нормально. А именно:

#include <variant>
#include <iostream>

template<template<typename...> class TEMPLATE, class... PARAMS>
void check(TEMPLATE<PARAMS...>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }

template<template<typename...> class TEMPLATE, class PARAM1> // <---- в этой перегрузке именно параметр, а не пак
void check(TEMPLATE<PARAM1>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }

template<typename T>
using using_fun = std::variant<int, T>;

int main() {
    using_fun<long> a;

    check<using_fun>(a);  // как вывести T для using_fun<T> из variant<int,long> ?
}
Re[4]: is_same_container
От: johny5 Новая Зеландия
Дата: 15.08.24 15:25
Оценка: +1
Здравствуйте, vopl, Вы писали:

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

Финальная версия
https://godbolt.org/z/fnzTznTbx

#include <iostream>
#include <vector>
#include <optional>
#include <variant>

template<typename T>
using using_fun = std::variant<int, T>;

//  tags
constexpr int MAX = 10;
template <int N> struct P : P<N+1> { };
template <> struct P<MAX> { };

template<typename T>
using using_fun = std::variant<int, T>;

template<template<typename...> class TEMPLATE>
std::false_type is_same_container_h(P<MAX>, ...) { std::cout << __PRETTY_FUNCTION__ << std::endl; return {}; }

template<template<typename...> class TEMPLATE, class PARAM1>
std::true_type is_same_container_h(P<0>, TEMPLATE<PARAM1>) { std::cout << __PRETTY_FUNCTION__ << std::endl; return {}; }

template<template<typename...> class TEMPLATE, class PARAM1, class PARAM2>
std::true_type is_same_container_h(P<1>, TEMPLATE<PARAM1, PARAM2>) { std::cout << __PRETTY_FUNCTION__ << std::endl; return {}; }

template<template<typename...> class TEMPLATE, class PARAM1, class PARAM2, class PARAM3>
std::true_type is_same_container_h(P<2>, TEMPLATE<PARAM1, PARAM2, PARAM3>) { std::cout << __PRETTY_FUNCTION__ << std::endl; return {}; }

///.... macro generate up to MAX-1]

template<template<typename...> class TEMPLATE, typename T>
auto is_same_container(T v) { return is_same_container_h<TEMPLATE>(P<0>{}, v); }

int main()
{
    std::vector<int> a;
    std::optional<int> c;
    using_fun<long> d;
    std::variant<std::string> e;

    std::cout << decltype(is_same_container<std::vector>(a))::value << "\n";
    std::cout << decltype(is_same_container<std::optional>(a))::value << "\n";
    std::cout << decltype(is_same_container<using_fun>(a))::value << "\n";
    std::cout << decltype(is_same_container<std::variant>(a))::value << "\n";
    std::cout << "\n";

    std::cout << decltype(is_same_container<std::vector>(c))::value << "\n";
    std::cout << decltype(is_same_container<std::optional>(c))::value << "\n";
    std::cout << decltype(is_same_container<using_fun>(c))::value << "\n";
    std::cout << decltype(is_same_container<std::variant>(c))::value << "\n";
    std::cout << "\n";

    std::cout << decltype(is_same_container<std::vector>(d))::value << "\n";
    std::cout << decltype(is_same_container<std::optional>(d))::value << "\n";
    std::cout << decltype(is_same_container<using_fun>(d))::value << "\n";  // <<--- must be 1
    std::cout << decltype(is_same_container<std::variant>(d))::value << "\n";
    std::cout << "\n";

    std::cout << decltype(is_same_container<std::vector>(e))::value << "\n";
    std::cout << decltype(is_same_container<std::optional>(e))::value << "\n";
    std::cout << decltype(is_same_container<using_fun>(e))::value << "\n";
    std::cout << decltype(is_same_container<std::variant>(e))::value << "\n";
    std::cout << "\n";
}


Таги нужны, потому что std::vector<> вызывает ambiguity, (он и как с 1м так и с 2мя шаблонными аргументами разрешается).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.