示例如下:
#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));
是可以的。
有谁能解释一下为什么吗?
在返回类型中对auto
施加概念约束的唯一效果是,如果将auto
推导为不满足概念的值,则程序将无法编译。除此之外,没有别的效果。
在您的示例中,multiply(4, 3)
将multiply
的参数类型推断为int
,因此p_val_1 * p_val_2
也属于int
类型,返回类型推断为int
。int
满足std::integral
,因此概念检查满足。
然后将multiply(4, 3)
的int
结果转换为其他内容完全不受返回类型或函数上的概念约束的影响。
使用大括号的转换是错误的,因为使用大括号的初始化不允许窄化转换,而整型到浮点型的转换总是允许窄化转换,除非源表达式是一个常量表达式,其值可以精确地用目标类型表示。在所有情况下,它都不是常量表达式,因为multiply
没有标记为constexpr
或consteval
。
所有这些都完全独立于multiply
返回类型的约束。