В следующем примере:
#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));
это так.
Кто-нибудь будет любезен объяснить, почему?
На вашей платформе (и на моей) int
сужается, чтобы вписаться в float
. Вы используете синтаксис с {}
, чтобы указать, что вы не хотите сужения.
Насколько я понимаю, поскольку я определяю, что возвращаемое значение multiply
должно удовлетворять std::integral
, поэтому объект, которому будет назначено это возвращение, также должен удовлетворять std::integral
. Если это понимание неверно, могу ли я сделать вывод, что нет смысла определять ограничение (требование) для возврата функции, так как объект, который получит значение, не должен удовлетворять тем же требованиям?
Единственный эффект наложения концептуального ограничения на 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 На обратном пути Result result = function()
есть 3 иногда разных объекта. (1) объект result
, (2) объект внутри function
, который возвращается, и (3) объект, возвращаемый из function
. Elision может уменьшить это до 1 объекта, но если они разных типов, этого не произойдет. И ваше ограничение распространяется только на (3); с auto
однако затем он выводится из (2) и идентичен ему и, следовательно, исключается в один объект.
«В моем (все еще) плохом понимании концепций не должно быть возможности присвоить результат умножения объекту, который не удовлетворял бы требованию std::integral»: на основании какого источника вы делаете это предположение? (Это неверно.)