LINUX.ORG.RU

Реализация метода шаблонного класса зависящая от того перегужена или нет одна из операций

 ,


1

1

Сабж

#include <iostream>

template <typename T> struct A{
	T x;

	T f(double y) const { return x*y; }

	// альтерантивная реализация, если T*double не определен
	// T f(double y) const { return x; } 
};

struct B{ int z; };

int main(){
	A<double> a;  std::cout<<a.f(1.)<<'\n';
	A<B> b;       std::cout<<b.f(1.)<<'\n';
}

как включать ту или иную реализацию A::f в зависимости от того есть или нету операции T*double ?

★★★★

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

думаю концепты покрывают полностью задачу.

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

Ну надо сделать так чтобы должен был кастоваться к тому типу над которым ты делаешь * :)

Линейную алгебру так не построить…

Спасибо за ссылку!

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

Спасибо!

11 плюсы говно, сейчас уже почти С++23…

Ага. Но у нас на кластере например

k60$ g++ -v
...
gcc версия 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)

Се ля ви.

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

Се ля ви.

Да, Я помню разрабы vcpkg грустили, что в RHEL 7 стоит gcc 4.8.5, и им для vcpkg пришлось реализовывать часть std::filesystem вместо использования стандартной библиотеки.

ripping out use of standard library feature from 4 years ago == 😭

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

Можно вопросов по коду?

template <typename T>
constexpr auto has_double_multiply(T* ) -> decltype(declval<T>() * 1.0, true) {
  return true;
}

template <typename T> constexpr bool has_double_multiply(...) { return false; }

ЕМНИП перегрузка с аргументами (…) имеет наинизший приоритет при разрешении (или как там оно называется), именно за счет этого оно и работает? А насколько нужен declval ?

 template <typename U = T,
            typename enable_if<has_double_multiply<U>(nullptr), int>::type = 0>
  U f(double y) const {
    return x * y;
  }

я не понимаю зачем вводить U (почему нельзя везде просто написать T), но без него не работает;-(

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

именно за счет этого оно и работает? А насколько нужен declvar ?

Да. Именно из-за наинизшего приоритета это и работает.

declvar нужен чтобы в контексте decltype появилась переменная типа T. Если есть ещё условия, например, тип T обязан также иметь конструктор по-умолчанию, то можно declvar заменить на T{}(поэтому чтобы ввести переменную типа double, я просто написал 1.0, а не через declvar<double>()), но тогда для типа T, который имеет оператор * с double но не имеет конструктора по-умолчанию вернётся false.

я не понимаю зачем вводить U (почему нельзя везде просто написать T), но без него не работает;-(

Если у нас будет только один тип T, то тип typename enable_if<has_double_multiply<U>(nullptr), int>::type будет использован во время инстанциирования A. enable_if работает только когда тип выводится компилятором. https://en.cppreference.com/w/cpp/language/template_argument_deduction

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

Все, понял. Спасибо большое!

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

А еще нубвопрос, T{} для int/float/double и пр. встроенных числовых типов дает инициализацию нулем?

AntonI ★★★★
() автор топика

Я надеюсь мне простят занудство, но в контексте решаемой ТС задачи действительно нужны шаблоны? Действительно это является наиболее рациональным решением?

Просто шаблоны сильно усложняют код из-за чрезмерной заабстрогированности. ИМХО их не стоит пихать везде, где только можно.

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

А еще нубвопрос, T{} для int/float/double и пр. встроенных числовых типов дает инициализацию нулем?

Да.

The effects of value initialization are:

  1. if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;
  2. if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
  3. if T is an array type, each element of the array is value-initialized;
  4. otherwise, the object is zero-initialized.

https://en.cppreference.com/w/cpp/language/value_initialization

https://en.cppreference.com/w/cpp/language/zero_initialization

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

А насколько нужен declval ?

это что бы для выражения как бы появилась переменная типа Т.

ох ну и ошибки же будут выдаваться.. вухахахаах

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

Я надеюсь мне простят занудство, но в контексте решаемой ТС задачи действительно нужны шаблоны? Действительно это является наиболее рациональным решением?

Если нужно, чтобы метод f был не шаблонным, то можно перенести шаблон выше, но тогда нужно будет дублировать все остальные методы…

Как-то так: https://gcc.godbolt.org/z/vo77nez8c

В С++17, всё было бы проще из-за if constexpr

template <typename T> 
struct A {
  T x{};

  T f(double y) const {
    if constexpr (has_double_multiply<T>(nullptr)) {
      return x * y;
    } else {
      return x;
    }
  }
};

https://gcc.godbolt.org/z/es7vYnaMW

А в С++20 можно бы сделать так:

template <typename T> 
struct A {
  T x{};

  T f(double y) const {
      if constexpr(requires{x*y;}) {
        return x * y;
      }
      else {
          return x;
      }
  }
};

https://gcc.godbolt.org/z/1on9bKfTK

Или так

template <typename T> 
struct A {
  T x{};

  T f(double y) const requires(requires{x*y;}){
        return x * y;
  }

  T f(double y) const requires(!requires{x*y;}){
        return x;
  }
};

https://gcc.godbolt.org/z/r78a5Kr47

В 14 плюсах всё было бы как в 11, но просто чуток короче:

Вместо:

template <typename U = T,
          typename enable_if<!has_double_multiply<U>(nullptr), int>::type = 0>
  U f(double /*y*/) const {
    return x;
  }

было бы

template <typename U = T, enable_if_t<!has_double_multiply_v<U>, int> = 0>
  U f(double /*y*/) const {
    return x;
  }

https://gcc.godbolt.org/z/d39c8M9ax

И ещё одно :),

if constexpr настолько лучше чем то что было раньше, что компиляторы ввели его для всех стандартов, https://gcc.godbolt.org/z/h94K58z44 , чтобы использовать для реализации стандартной библиотеки в режиме С++11/С++14 или С++98. А SFINAE/tag dispatch выжигать из исходников где только можно :)

Скоро и на концепты заменят где удобнее, как компиляторы немного обновятся:

https://github.com/microsoft/STL/issues/602

https://github.com/microsoft/STL/issues/189

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

В https://gcc.godbolt.org/z/d39c8M9ax нужно ещё дописать inline или static, чтобы не было ошибок линковки когда переменная будет инстанциироваться в разных объектных файлах.

вместо

template <typename T>
constexpr bool has_double_multiply_v = has_double_multiply<T>(nullptr);

нужно

#if __cplusplus > 201402
#define INLINE inline
#else
#define INLINE static
#endif

template <typename T>
INLINE constexpr bool has_double_multiply_v = has_double_multiply<T>(nullptr);
fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

if constexpr настолько лучше чем то что было раньше, что компиляторы ввели его для всех стандартов, https://gcc.godbolt.org/z/h94K58z44 , чтобы использовать для реализации стандартной библиотеки в режиме С++11/С++14 или С++98. А SFINAE/tag dispatch выжигать из исходников где только можно :)

Так… И снова спасибо, я глубоко задумался. Этой штуки мне ужасно не хватало.

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

ох ну и ошибки же будут выдаваться.. вухахахаах

Вообще то sfinae+static_assert это один из способов получать вменяемые ошибки а не 10 экранов фиг знает чего

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

С учетом того что что А толстый шаблонный класс (и это по делу) без шаблонов тут никак:-)

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

Вот ещё подумал, что лучше проверять ещё возвращаемый тип T{} * 1.0:

template <typename T>
constexpr auto has_double_multiply(T *) -> decltype(declval<T>() * 1.0, true) {
  return is_convertible<decltype(declval<T>() * 1.0), T>::value;
}

https://gcc.godbolt.org/z/8s6YznT4f

fsb4000 ★★★★★
()

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

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

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

не надо ломать через коленку свойства посторонних классов своими типа «алгоритмами».

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

Ну пошла хвилософия…:-(

А довольно развесистая структура. Для некоторых Т некоторые ее методы не работают, это нормально - в случае попытки использовать такой метод юзер получает ошибку компиляции. Беда в том, что при инстацировании в питон (что иногда бывает) инстацируются ВСЕ методы А.

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

ну может пример неудачный у вас…

но это то треш -

// альтерантивная реализация, если T*double не определен
	// T f(double y) const { return x; } 

то есть «альтернативная реализация» молча возвращает какую-то пургу и спокойно работает дальше.

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

ЛОР такой ЛОР… Вы понятия не имеете о специфике решаемой задачи, но вот мнение о том что пурга а что нет и как задачу надо решать а как нет - обо всем этом Вы мнение имеете.

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

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

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

Линейную алгебру так не построить…

Дизайн твой плох, юный падован )

Нужно не пытаться угадать «реализован ли оператор», а брать все в свои руки и определять систему типов с операциями.

Я б скорее с free functions делал такое.

product (scalar, scalar);
product (scalar, vector);

struct scalar{
  scalar(auto n) // conversion ctor
};

И т.п.

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

Дизайн твой плох, юный падован )

invy ★★★★★ (12.09.21 00:19:15) Телепат

no comment.

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