Концепции C++ и проблема преобразования

В следующем примере:

#include <concepts>
#include <cstdint>
#include <iostream>

using namespace std;

integral auto multiply(integral auto p_val_1, integral auto p_val_2) {
  return p_val_1 * p_val_2;
}

int main() {
  {
    float f{multiply(4, 3)};
    cout << "f = " << f << endl;
  }

  {
    float f(multiply(4, 3));
    cout << "f = " << f << endl;
  }

  {
    constexpr uint16_t i1{4};
    constexpr uint16_t i2{3};
    float f{multiply(i1, i2)};
    cout << "f = " << f << endl;
  }

  {
    constexpr int i1{4};
    constexpr int i2{3};
    float f(multiply(i1, i2));
    cout << "f = " << f << endl;
  }

  return 0;
}

gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0, в float f{multiply(4, 3)};, отчеты

main.cpp:13:13: Non-constant-expression cannot be narrowed from type 'int' to 'float' in initializer list (fix available)
insert an explicit cast to silence this issue

а в float f{multiply(i1, i2)}; он сообщает

main.cpp:25:13: Non-constant-expression cannot be narrowed from type 'int' to 'float' in initializer list (fix available)
insert an explicit cast to silence this issue

В моем (все еще) плохом понимании концепций не должно быть возможности присвоить результат multiply объекту, который не удовлетворяет требованию std::integral, но с помощью float f(multiply(i1, i2)); или float f(multiply(4, 3)); это так.

Кто-нибудь будет любезен объяснить, почему?

«В моем (все еще) плохом понимании концепций не должно быть возможности присвоить результат умножения объекту, который не удовлетворял бы требованию std::integral»: на основании какого источника вы делаете это предположение? (Это неверно.)

user17732522 08.04.2023 22:34

На вашей платформе (и на моей) int сужается, чтобы вписаться в float. Вы используете синтаксис с {}, чтобы указать, что вы не хотите сужения.

Eljay 08.04.2023 22:36

Насколько я понимаю, поскольку я определяю, что возвращаемое значение multiply должно удовлетворять std::integral, поэтому объект, которому будет назначено это возвращение, также должен удовлетворять std::integral. Если это понимание неверно, могу ли я сделать вывод, что нет смысла определять ограничение (требование) для возврата функции, так как объект, который получит значение, не должен удовлетворять тем же требованиям?

canellas 08.04.2023 22:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
86
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

В вашем примере multiply(4, 3) выводит типы параметров multiply как в int, так что p_val_1 * p_val_2 также имеет тип int, а возвращаемый тип выводится в int. int удовлетворяет std::integral, поэтому проверка концепции выполнена.

То, что вы затем преобразуете int результат multiply(4, 3) во что-то другое, совершенно не зависит от концептуального ограничения типа возвращаемого значения или функции вообще.


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

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

Все это совершенно не зависит от ограничения на возвращаемый тип multiply.

«То, что вы затем конвертируете результат int умножения (4, 3) во что-то другое, совершенно не зависит от концептуального ограничения на тип возвращаемого значения». Это моя ошибка: я думал, что определение ограничения на возврат multiply заставит объект, получающий возврат, придерживаться того же ограничения.

canellas 08.04.2023 22:44

@canellas На обратном пути Result result = function() есть 3 иногда разных объекта. (1) объект result, (2) объект внутри function, который возвращается, и (3) объект, возвращаемый из function. Elision может уменьшить это до 1 объекта, но если они разных типов, этого не произойдет. И ваше ограничение распространяется только на (3); с auto однако затем он выводится из (2) и идентичен ему и, следовательно, исключается в один объект.

Yakk - Adam Nevraumont 10.04.2023 16:03

Другие вопросы по теме