不一致的隐式转换行为



我遇到了一个无法理解c++中隐式转换行为的情况。代码如下:

template <bool b, int i, unsigned... us>
void foo() {}
template <int i, unsigned... us>
void foo() {return foo<false, i, us...>();}
int main()
{
foo<true, -1, 0ul, 1ul, 2ul>(); // compiles with clang and gcc > 8 (gcc<=8 gives ambiguous function call)
foo<true, +1, 0ul, 1ul, 2ul>(); // ambiguous function call error
foo<-1, 0ul, 1ul, 3ul>(); // compiles with clang and gcc > 8 (gcc<=8 gives ambiguous function call)
foo<+1, 0ul, 1ul, 3ul>(); // ambiguous function call error
}

对于main()中的第1行和第2行,编译器似乎只对正整数隐式地将int转换为unsigned(这是可以理解的,但有规则吗?(

对于main()中的第3行和第4行,编译器似乎仅对正整数隐式地将int转换为bool。我不明白为什么。

背景:理想情况下,我希望得到一个类似的结构,它可以有效地为bool模板参数创建一个默认值。但由于参数包和c++隐式内置转换,这似乎很困难。

非类型模板参数必须是模板参数类型的转换常量表达式

转换的常量表达式特别不允许缩小转换范围,在积分类型之间计算常量表达式的情况下,这意味着如果转换会更改数值,则不允许进行转换。

这是特定于转换的常量表达式要求的。如果这些是函数参数和自变量,那么-1unsigned的隐式转换是允许的,并且它也有一个定义明确的结果,尽管显然与-1的数值不同。

因此,在foo<true, -1, 0ul, 1ul, 2ul>();中,需要unsigned处于-1的那个位置的模板作为过载候选者确实是不可行的。

布尔转换,这意味着从其他标量类型到bool的转换根本没有列在转换后的常量表达式允许的转换列表中(请参见[expr.const]/10(,据我所知,这应该会使bool在第一个位置的重载对foo<-1, 0ul, 1ul, 3ul>();foo<+1, 0ul, 1ul, 3ul>();都不可行。我不知道为什么编纂者认为后者模棱两可。根据CWG问题1407的无缺陷解决方案,从int模板参数到bool模板参数的转换确实是不允许的,但编译器的行为不一致,似乎将其视为范围为0/1的积分类型。

我不知道GCC<8已经。我想这只是一个错误,它认为过载是模糊的。

@user17732522的回答完全解释了转换行为。

这里有一个破解来实现默认模板参数为bool模板参数的函数:

template<bool b>
struct Dummy {};
template <int i, unsigned... us, bool b>
void foo(Dummy<b>) {}
template <int i, unsigned... us>
void foo() {return foo<i, us...>(Dummy<false>{});}
int main()
{
foo<-1, 0ul, 1ul, 2ul>(Dummy<true>{});
foo<+1, 0ul, 1ul, 2ul>(Dummy<true>{});
foo<-1, 0ul, 1ul, 3ul>();
foo<+1, 0ul, 1ul, 3ul>();
}

最新更新