Почему вычеты для параметров шаблона не используются только как возвращаемый тип? - PullRequest
4 голосов
/ 01 августа 2020

Если я не использую временный параметр (тип) в списке аргументов функции -> только как возвращаемый тип, то вычитания не будет:

template <typename T>
T zero() { return 0; }

int main()
{
    int x = zero();
}

дает:

a.cpp:15:18: error: no matching function for call to ‘zero()’
     int x = zero();
                  ^
a.cpp:11:3: note: candidate: ‘template<class T> T zero()’
 T zero() { return 0; }
   ^~~~
a.cpp:11:3: note:   template argument deduction/substitution failed:
a.cpp:15:18: note:   couldn't deduce template parameter ‘T’
     int x = zero();

Единственный способ компиляции - указать тип шаблона в угловых скобках:

template <typename T>
T zero() { return 0; }

int main()
{
    int x = zero<int>();
}
  1. Итак, мой вопрос, почему g ++ может выводить тип из список аргументов функции шаблона, но не может вывести его из возвращаемого типа (который также известен компилятору при компиляции main, поэтому он знает тип) .

  2. указание типа в угловых скобках для функции шаблона является произвольным (из-за дедукции), когда функция шаблона использует типы шаблона в своем списке аргументов? Поэтому следует ли всегда указывать тип в фигурных скобках, независимо от того, как объявлена ​​функция?

Второй вопрос плохо читается. Проще говоря -> следует ли мне использовать foo<T>(arg, ...) (указать тип) каждый раз, независимо от объявления функции? Даже если это может быть выведено компилятором, но я все равно предоставлю тип для хорошей практики?

Ответы [ 4 ]

6 голосов
/ 01 августа 2020

Обычно невозможно вывести функцию на основе ее возвращаемого типа. Но если вы используете автоматическую c функцию преобразования типов c ++, вы можете достичь того, что вам нужно:

template <typename T>
T zero() { return 1; }

template <>
float zero<float>() { return 3.0f; }

struct Zero
{
    template<typename T>
    operator T()
    {
        return zero<T>();
    }
};

int main()
{
    int x = Zero();
    float y = Zero();
    return x + y;
}

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

4 голосов
/ 01 августа 2020

Итак, мой вопрос в том, почему g ++ может определить тип из списка аргументов функции-шаблона.

G CC следует правилам, установленным стандартом C ++.

должен ли я использовать foo (arg, ...) (указать тип) каждый раз, независимо от объявления функции?

Это зависит от того, чего вы хотите достичь. Если вы хотите быть откровенным, сделайте это. Это было бы похоже на вызов функции foo_T() в C, у которой нет ни шаблонов, ни перегрузок. Однако, если вы хотите, чтобы ваш код был обобщенным c (например, потому что он вызывается в самом шаблоне или потому, что вы хотите, чтобы его было легче изменить при будущих изменениях типа), то вы предпочли бы избегать явного написания типа.

Другой вариант - использовать перегрузку, а не шаблон. То, что вы используете, опять же, зависит от того, что вы хотите, и вашего варианта использования.

Наконец, вы также можете использовать auto вместо:

auto zero() { return 0; }

Сказав это, для подписей / интерфейсов, я думаю, что лучше всего использовать явные типы везде , если нет причины не делать этого (например, это должен быть шаблон):

int zero() { return 0; }
2 голосов
/ 01 августа 2020

Вопрос 1

Хотя может быть относительно легко изменить правила в ваших тривиальных случаях, обычно это не так.

Рассмотрим такой случай:

template <A, B, C> A f(B b, C c) { ... } // #1
int f(int a, int b) { ... } // #2
int f(int a, double b) { ... } // #3

И такой вызов:

double x = f(1, 2.0);  // Call #1 or #3?

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

Стандарт обычно избегают изменения языка таким образом, чтобы могли произойти неожиданные тихие изменения (или сделать законным код становится неоднозначным) .

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

Вопрос 2

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

Альтернативы

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

template <class T> void zero(T &t) { t = 0; }  // set_zero would be more readable

int x;
zero(x);

Я бы не считал это решение подходящим для этого случая, поскольку следующее намного яснее:

auto x = zero<int>();

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

int x = {};

или

int x {};
0 голосов
/ 01 августа 2020

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

Эта функция не является шаблоном, когда вся информация необходимо, чтобы определить аргумент шаблона в функции . Он должен просто вернуть int, точка. Если вы не хотите указывать тип возвращаемого значения по какой-либо причине, это то, для чего предназначен auto: он выполнит вычитание, которое вы ищете.

С другой стороны, если вы хотите 0 для преобразования в разные типы в зависимости от аргумента шаблона, тогда это имеет больше смысла, и у вас уже есть решение для этого: укажите аргумент шаблона (как компьютер мог его угадать?). В этом конкретном примере вам будет лучше просто конвертировать на сайте callite, но, вероятно, вы имеете в виду более сложную логику c.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...