Специализация метода. Нужно устранить ошибку
От: SVV Беларусь  
Дата: 23.08.22 12:46
Оценка:
Всем привет, есть такой код:
using namespace std;
using u1 = unsigned char;

template<typename T>struct TypeH;
using TypeH1 = TypeH<int>;
using TypeH2 = TypeH<char>;

template<typename T>
struct STest4
{
  template<typename H> void foo();// ключевой момент
  template<> void foo<TypeH1>(){ cout << "TypeH1" << endl; }
};
//template<>
template<typename T> void STest4<T>::foo<TypeH2>(){ cout << "TypeH2" << endl; }//error C2768: 'STest4<T>::foo': illegal use of explicit template arguments

int Test4()
{
  STest4<float> st4;
  st4.foo<TypeH1>();
  st4.foo<TypeH2>();
  return 0;
}

как правильно написать специализацию метода?
Re: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 23.08.22 13:21
Оценка: 6 (1)
Здравствуйте, SVV, Вы писали:

SVV>как правильно написать специализацию метода?


Напрямую — никак. Это будет частичная специализация, язык такое не позволяет. Но, можно нечто похожее организовать через SFINAE или через концепты

#include <iostream>

using namespace std;
using u1 = unsigned char;

template<typename T>struct TypeH;
using TypeH1 = TypeH<int>;
using TypeH2 = TypeH<char>;

template<typename T>
struct STest4
{
    //на концептах
    template<typename H> void foo() requires std::is_same_v<H, TypeH1>
    {
        cout << "TypeH1" << endl;
    }

    template<typename H> void foo() requires std::is_same_v<H, TypeH2>;

    //на SFINAE
    template<typename H> std::enable_if_t<std::is_same_v<H, TypeH1>> foo2()
    {
        cout << "TypeH1" << endl;
    }

    template<typename H> std::enable_if_t<std::is_same_v<H, TypeH2>>  foo2();
};

template<typename T>
template<typename H>
void STest4<T>::foo() requires std::is_same_v<H, TypeH2>
{
    cout << "TypeH1" << endl;
}

template<typename T>
template<typename H>
std::enable_if_t<std::is_same_v<H, TypeH2>> STest4<T>::foo2()
{
    cout << "TypeH2" << endl;
}

int Test4()
{
  STest4<float> st4;
  st4.foo<TypeH1>();
  st4.foo<TypeH2>();
  st4.foo2<TypeH1>();
  st4.foo2<TypeH2>();
  return 0;
}


Еще можно вынести специализацию в другой класс и его уже специализировать, а foo оставить обобщенным..
Re[2]: Специализация метода. Нужно устранить ошибку
От: SVV Беларусь  
Дата: 23.08.22 14:40
Оценка:
Здравствуйте, vopl, Вы писали:

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


SVV>>как правильно написать специализацию метода?


V>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет. Но, можно нечто похожее организовать через SFINAE или через концепты

...
V>Еще можно вынести специализацию в другой класс и его уже специализировать, а foo оставить обобщенным..

А как подобное провернуть с конструктором? Проблема с вызовом такого конструктора:
template<typename T>
struct STest4
{
  template<typename H> STest4() requires is_same_v<H, TypeH1> { cout << "ctor(TypeH1)" << endl; }
  template<typename H> STest4() requires is_same_v<H, TypeH2> { cout << "ctor(TypeH2)" << endl; }
  ...
};

STest4<float> st5_1<TypeH1>();//warning C4930: 'STest4<float> st5_1(void)': prototyped function not called (was a variable definition intended?)
STest4<float> st5_2<TypeH2>();//warning C4930: 'STest4<float> st5_1(void)': prototyped function not called (was a variable definition intended?)


код компилируется, но конструктор не вызывается.
Re[3]: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 23.08.22 16:41
Оценка: 6 (1)
Здравствуйте, SVV, Вы писали:

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


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


SVV>>>как правильно написать специализацию метода?


V>>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет. Но, можно нечто похожее организовать через SFINAE или через концепты

SVV>...
V>>Еще можно вынести специализацию в другой класс и его уже специализировать, а foo оставить обобщенным..

SVV>А как подобное провернуть с конструктором? Проблема с вызовом такого конструктора:

SVV>
SVV>template<typename T>
SVV>struct STest4
SVV>{
SVV>  template<typename H> STest4() requires is_same_v<H, TypeH1> { cout << "ctor(TypeH1)" << endl; }
SVV>  template<typename H> STest4() requires is_same_v<H, TypeH2> { cout << "ctor(TypeH2)" << endl; }
SVV>  ...
SVV>};

SVV>STest4<float> st5_1<TypeH1>();//warning C4930: 'STest4<float> st5_1(void)': prototyped function not called (was a variable definition intended?)
SVV>STest4<float> st5_2<TypeH2>();//warning C4930: 'STest4<float> st5_1(void)': prototyped function not called (was a variable definition intended?)
SVV>


SVV>код компилируется, но конструктор не вызывается.


Не вызывается, потому что st5_1 и st5_2 — это не объявления объектов, это такие функции. Примерно как тут https://cplusplus.com/doc/tutorial/classes/ , поищи там по слову oops

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

using TypeH1 = int;
using TypeH2 = char;

template <class T> struct Tag{};

template<typename T>
struct STest4
{
  template<typename H> STest4(Tag<H>) requires std::is_same_v<H, TypeH1> { std::cout << "ctor(TypeH1)" << std::endl; }
  template<typename H> STest4(Tag<H>) requires std::is_same_v<H, TypeH2> { std::cout << "ctor(TypeH2)" << std::endl; }
};

int main(int c_argc, char* c_argv[])
{
    STest4<float> st5_1{Tag<TypeH1>{}};
    STest4<float> st5_2{Tag<TypeH2>{}};
    return 0;
}
Re: Специализация метода. Нужно устранить ошибку
От: rg45 СССР  
Дата: 24.08.22 08:19
Оценка: +1
Здравствуйте, SVV, Вы писали:

SVV>Всем привет, есть такой код:

  Исходный пример
SVV>
SVV>using namespace std;
SVV>using u1 = unsigned char;

SVV>template<typename T>struct TypeH;
SVV>using TypeH1 = TypeH<int>;
SVV>using TypeH2 = TypeH<char>;

SVV>template<typename T>
SVV>struct STest4
SVV>{
SVV>  template<typename H> void foo();// ключевой момент
SVV>  template<> void foo<TypeH1>(){ cout << "TypeH1" << endl; }
SVV>};
SVV>//template<>
SVV>template<typename T> void STest4<T>::foo<TypeH2>(){ cout << "TypeH2" << endl; }//error C2768: 'STest4<T>::foo': illegal use of explicit template arguments

SVV>int Test4()
SVV>{
SVV>  STest4<float> st4;
SVV>  st4.foo<TypeH1>();
SVV>  st4.foo<TypeH2>();
SVV>  return 0;
SVV>}
SVV>

SVV>как правильно написать специализацию метода?

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

http://coliru.stacked-crooked.com/a/9ff8360b3fef1f76

#include <iostream>

using u1 = unsigned char;

template<typename T>struct TypeH;
using TypeH1 = TypeH<int>;
using TypeH2 = TypeH<char>;

template <typename>
struct TypeHolder{};

template<typename T>
struct STest4
{
  template<typename H> void foo() { foo(TypeHolder<H>{}); }// ключевой момент
  void foo(TypeHolder<TypeH1>){ std::cout << "TypeH1" << std::endl; }
  void foo(TypeHolder<TypeH2>){ std::cout << "TypeH2" << std::endl; }
};

int main()
{
  STest4<float> st4;
  st4.foo<TypeH1>();
  st4.foo<TypeH2>();
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.08.2022 8:23 rg45 . Предыдущая версия .
Re[4]: Специализация метода. Нужно устранить ошибку
От: SVV Беларусь  
Дата: 24.08.22 08:36
Оценка:
Здравствуйте, vopl, Вы писали:
...
V>
...
V>  template<typename H> STest4(Tag<H>) requires std::is_same_v<H, TypeH1> { std::cout << "ctor(TypeH1)" << std::endl; }
V>  template<typename H> STest4(Tag<H>) requires std::is_same_v<H, TypeH2> { std::cout << "ctor(TypeH2)" << std::endl; }
...
V>

чтобы всё заработало конструкторы оформил так:
STest4(Tag<TypeH1>) { cout << "ctor(TypeH1)" << endl; }
STest4(Tag<TypeH2>) { cout << "ctor(TypeH2)" << endl; }

Мне подходит. Спасибо.
Получается, что моя проблемы была в том, что я забыл про фигурные скобки и что нужно опредлить пустую Tag (... Tag{}; а не ... Tag; )
Я понимал, что вызов st5_1 выглядит как определение функции, но думал что, может, есть способ сказать компилятору, что это именно определения объектов. Нет так нет.
Re[2]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 10:09
Оценка:
V>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет.

Как можно понять, что template<typename T> void STest4<T>::foo<TypeH2>() это частичная специализация?
Re[3]: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 24.08.22 10:30
Оценка:
Здравствуйте, σ, Вы писали:

V>>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет.


σ>Как можно понять, что template<typename T> void STest4<T>::foo<TypeH2>() это частичная специализация?


Ну, понять это достаточно просто: если после подстановки аргументов шаблон воплощается в тип/значение — то это полная специализация, а если все еще остается шаблоном — то это частичная. В приведенном куске метод foo принадлежит наблону STest4<T>, то есть сам продолжает быть шаблоном, занчт — частичная специализация. Еще примеры:

template <class T>
struct STest4
{
    template <class X> void foo();
};

// полная
template <> template <> void STest4<int>::foo<int>() {}

// полная
template <> template <> void STest4<int>::foo<char>(){}

// частичная
// template <> template <class X> void STest4<int>::foo<X*>(){}

// частичная
// template <class T> template <> void STest4<T>::foo<int>(){}


Тут более формально описано https://en.cppreference.com/w/cpp/language/partial_specialization. Частичная специализация разрешена только для классовых типов и переменных:

Partial template specialization
Allows customizing class and variable (since C++14) templates for a given category of template arguments.

Отредактировано 24.08.2022 10:31 vopl . Предыдущая версия .
Re[4]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 12:37
Оценка:
V>>>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет.

σ>>Как можно понять, что template<typename T> void STest4<T>::foo<TypeH2>() это частичная специализация?


V>Ну, понять это достаточно просто: если после подстановки аргументов шаблон воплощается в тип/значение — то это полная специализация, а если все еще остается шаблоном — то это частичная. В приведенном куске метод foo принадлежит наблону STest4<T>, то есть сам продолжает быть шаблоном, занчт — частичная специализация.


Интересные методы, конечно. Я думал критерий это <TypeH2> после foo (http://eel.is/c++draft/temp.decls.general#2.sentence-3)
Re[5]: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 24.08.22 15:00
Оценка:
Здравствуйте, σ, Вы писали:

V>>>>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет.


σ>>>Как можно понять, что template<typename T> void STest4<T>::foo<TypeH2>() это частичная специализация?


V>>Ну, понять это достаточно просто: если после подстановки аргументов шаблон воплощается в тип/значение — то это полная специализация, а если все еще остается шаблоном — то это частичная. В приведенном куске метод foo принадлежит наблону STest4<T>, то есть сам продолжает быть шаблоном, занчт — частичная специализация.


σ>Интересные методы, конечно. Я думал критерий это <TypeH2> после foo (http://eel.is/c++draft/temp.decls.general#2.sentence-3)


Оо.. Я чето от такой формулировки немного в ступоре ..

По моей формуле лучше сюда смотреть http://eel.is/c++draft/temp.spec.partial.general#1

A partial specialization of a template provides an alternative definition of the template that is used instead of the primary definition when the arguments in a specialization match those given in the partial specialization.

Или простыми словами — из шаблона сделали чуть более специализированный шаблон.

и сюда http://eel.is/c++draft/temp.expl.spec#1

An explicit specialization of ... can be declared by a declaration introduced by template<> ...

То есть, перечень шаблонных параметров пуст (следовательно все аргументы конкретны), то есть ничего шаблонного в нем не осталось и получилась уже нешаблонная инстанция — тип/значение/функция.
Re[6]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 15:11
Оценка:
V>По моей формуле лучше сюда смотреть http://eel.is/c++draft/temp.spec.partial.general#1
V>

A partial specialization of a template provides an alternative definition of the template

Тут говорится что partial specialization делает, а не что это такое.
Re[4]: Специализация метода. Нужно устранить ошибку
От: Андрей Тарасевич Беларусь  
Дата: 24.08.22 15:24
Оценка: 9 (1)
Здравствуйте, vopl, Вы писали:

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


V>>>Напрямую — никак. Это будет частичная специализация, язык такое не позволяет.


σ>>Как можно понять, что template<typename T> void STest4<T>::foo<TypeH2>() это частичная специализация?


V>Ну, понять это достаточно просто: если после подстановки аргументов шаблон воплощается в тип/значение — то это полная специализация, а если все еще остается шаблоном — то это частичная. В приведенном куске метод foo принадлежит наблону STest4<T>, то есть сам продолжает быть шаблоном, занчт — частичная специализация. Еще примеры:


Это попытка ответа с точки зрения rationale, т.е. "длинный ответ" на вопрос почему это не разрешается. И вряд ли это точное/полное rationale.

Прямой "короткий" ответ на вопрос не имеет никакого отношения ни к каким частичным специализациям функций. Язык просто запрещает объявление явных специализаций любых шаблонов внутри определения охватывающего шаблона класса. Этот запрет в одинаковой мере распространяется и на классы, и на функции, т.е. отсутствие частичной специализации функций тут вообще ни при чем. Вы получите ту же ошибку и на

template <typename T> struct S
{
  template <typename U> struct N {};
  template <> struct N<int> {}; // <- Ошибка
};


(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)

Явные специализации шаблонов-членов должны объявляться в namespace scope и должны делаться только для конкретных специализаций охватывающего шаблона. То есть, подчеркиваю, это ограничение в равное мере распространяется и на вложенные шаблоны классов, то есть отсутствие частичной специализации функций тут вообще ни при чем.

template <typename T> struct S
{
  template <typename U> struct N {};
};

template <> template <> struct S<int>::N<double> {}; // <- Так можно

template <typename T> template <> struct S<T>::N<double> {}; // <- А вот так нельзя
Best regards,
Андрей Тарасевич
Отредактировано 24.08.2022 15:31 Андрей Тарасевич . Предыдущая версия . Еще …
Отредактировано 24.08.2022 15:28 Андрей Тарасевич . Предыдущая версия .
Отредактировано 24.08.2022 15:26 Андрей Тарасевич . Предыдущая версия .
Re[5]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 15:40
Оценка: 22 (1) +1
АТ>Прямой "короткий" ответ на вопрос не имеет никакого отношения ни к каким частичным специализациям функций. Язык просто запрещает объявление явных специализаций любых шаблонов внутри определения охватывающего шаблона класса. Этот запрет в одинаковой мере распространяется и на классы, и на функции, т.е. отсутствие частичной специализации функций тут вообще ни при чем.

Кхм. Ващет, вопрос был про template<typename T> void STest4<T>::foo<TypeH2>() { ... } (вне класса), а не про template<> void foo<TypeH1> (внутри класса)

АТ>
template <typename T> struct S
{
  template <typename U> struct N {};
  template <> struct N<int> {}; // <- Ошибка
};


АТ>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)


Неудивительно, его больше 5 лет как нет: https://cplusplus.github.io/CWG/issues/727.html
И запрет выпилили в статусе defect report-а, т.е. с ретроактивным применением выпиливания.
Re[5]: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 24.08.22 15:48
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Прямой "короткий" ответ на вопрос не имеет никакого отношения ни к каким частичным специализациям функций. Язык просто запрещает объявление явных специализаций любых шаблонов внутри определения охватывающего шаблона класса. Этот запрет в одинаковой мере распространяется и на классы, и на функции, т.е. отсутствие частичной специализации функций тут вообще ни при чем.


Черт, точно!

АТ>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)


Похоже это тут https://eel.is/c++draft/temp.expl.spec#17.sentence-1

the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well

Re[6]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 15:51
Оценка:
АТ>>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)

V>Похоже это тут https://eel.is/c++draft/temp.expl.spec#17.sentence-1

V>

the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well


> *смотрит на explicit specialization в class scope*

> *кидает параграф про "an explicit specialization ... that appears in namespace scope"*

Re[7]: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 24.08.22 15:54
Оценка:
Здравствуйте, σ, Вы писали:

V>>По моей формуле лучше сюда смотреть http://eel.is/c++draft/temp.spec.partial.general#1

V>>

A partial specialization of a template provides an alternative definition of the template

σ>Тут говорится что partial specialization делает, а не что это такое.

Ага. Определение частичной специализации судя по всему вот это
σ> Интересные методы, конечно. Я думал критерий это <TypeH2> после foo (http://eel.is/c++draft/temp.decls.general#2.sentence-3)
Но, мне данное определение что то не ложится в голову. Может кто из гуру пояснит что тут как понимать ..
Re[7]: Специализация метода. Нужно устранить ошибку
От: vopl Россия  
Дата: 24.08.22 16:01
Оценка:
Здравствуйте, σ, Вы писали:

АТ>>>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)


V>>Похоже это тут https://eel.is/c++draft/temp.expl.spec#17.sentence-1

V>>

the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well


>> *смотрит на explicit specialization в class scope*

>> *кидает параграф про "an explicit specialization ... that appears in namespace scope"*

σ>


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

АТ>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)

АТ>Явные специализации шаблонов-членов должны объявляться в namespace scope и должны делаться только для конкретных специализаций охватывающего шаблона. То есть, подчеркиваю, это ограничение в равное мере распространяется и на вложенные шаблоны классов, то есть отсутствие частичной специализации функций тут вообще ни при чем.
АТ>
АТ>template <typename T> struct S
АТ>{
АТ>  template <typename U> struct N {};
АТ>};

АТ>template <> template <> struct S<int>::N<double> {}; // <- Так можно

АТ>template <typename T> template <> struct S<T>::N<double> {}; // <- А вот так нельзя
АТ>
Re[8]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 16:04
Оценка:
V>Я отвечал на этот кусок:

Предлагаю научиться пользоваться цитированием, т.к. было написано
АТ>>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)
V>Похоже это тут https://eel.is/c++draft/temp.expl.spec#17.sentence-1

Где
АТ>>(Я все время забываю, где этот запрет сформулирован в стандарте и навскидку не могу его найти.)
относится к
template <typename T> struct S
{
  template <typename U> struct N {};
  template <> struct N<int> {}; // <- Ошибка
};
Re[5]: Специализация метода. Нужно устранить ошибку
От: rg45 СССР  
Дата: 24.08.22 16:09
Оценка: +1
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Прямой "короткий" ответ на вопрос не имеет никакого отношения ни к каким частичным специализациям функций. Язык просто запрещает объявление явных специализаций любых шаблонов внутри определения охватывающего шаблона класса. Этот запрет в одинаковой мере распространяется и на классы, и на функции, т.е. отсутствие частичной специализации функций тут вообще ни при чем.


Но при этом частичные специализации можно делать прямо в классе:

struct A
{
    template <typename> struct Foo;
    
    template <typename T> struct Foo<T*> {}; // OK
    template <> struct Foo<int> {}; // error: explicit specialization in non-namespace scope 'struct A'
};


Это просто для полноты картины.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[6]: Специализация метода. Нужно устранить ошибку
От: σ  
Дата: 24.08.22 16:19
Оценка: 24 (3)
R> error: explicit specialization in non-namespace scope 'struct A'

Баг GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282

Это просто для полноты картины.
Отредактировано 24.08.2022 16:19 σ . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.