gcc vs clang:静态转换时在未使用的模板专业化中解析的 noexcept
gcc vs clang: noexcept parsed in unused template specialization when static casting
我正在尝试将函数指针静态转换为特定函数重载,但似乎 clang 仍在解析(未使用的)模板特化的 noexcept 语句,因此生成编译器错误。
如果未使用相应的函数重载,GCC 似乎并不关心 noexcept。
template<typename T>
void fun( T ) noexcept( T(1) ){}
void fun(int) {}
void fun(int*) {}
int main () {
int a;
fun(&a); //calling works fine
fun(a);
static_cast<void(*)(int*)>(&fun); // static casting doesn't
}
哪个编译器错了?
当将函数指针转换为特定重载时,该标准是否具体规定了应该编译的内容?
编辑:
在 Maxim 的评论之后,我将第二个 noexcept 放回示例中,它在 gcc 和 clang 上编译。
所以这是另一个例子,它实际上失败了 noexcept(noexcept(...))
当谈到获取函数模板的地址时,正如您的静态转换所做的那样,标准有以下相关段落:
[over.over] Address of overloaded function (emphasis mine)
2 If the name is a function template, template argument deduction is done ([temp.deduct.funcaddr]), and if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization, which is added to the set of overloaded functions considered.
模板参数推导(不考虑异常规范),成功。那么整个函数就被实例化了。只有在这个实例化之后,编译器才会检查是否有更好的 non-template 匹配:
4 If more than one function is selected, any function template specializations in the set are eliminated if the set also contains a function that is not a function template specialization
这与函数调用的情况不同,在函数调用中,重载决议将根据 non-template 重载的存在提前放弃函数特化。在这种情况下,标准在确定确实需要它之前需要函数左值的存在。我不确定它是否可以被视为措辞缺陷,或者有更高的原因,但它看起来确实 un-intuitive.
所以结论是 Clang 没有错,但 GCC 的行为更直观。
有点off-topic,但值得一提的是,如果您想说函数模板 fun
只有在 T(1)
不抛出时才为 noexcept
,然后
template<typename T>
void fun( T ) noexcept( T(1) ){}
应该是
template<typename T>
void fun( T ) noexcept(noexcept(T(1))){}
[over.over] ¶1 A function with type F
is selected for the function type FT
of the target type required in the context if F
(after possibly applying the function pointer conversion ([conv.fctptr])) is identical to FT
.
[conv.fctptr] A prvalue of type "pointer to noexcept
function" can be converted to a prvalue of type "pointer to function".
这意味着,在完成模板参数推导后 ([over.over] ¶2) 确定要添加到重载集中的模板函数特化,模板函数特化和 non-template 函数的类型都必须与目标类型进行比较,以决定应该“选择”它们中的哪一个。
tie-breaker 优先使用 non-template 函数 (¶4) 仅当两个函数的类型都匹配目标类型时才会生效(即如果选择了多个功能)。
void(*)(int*)
和 void(*)(int*)noexcept
是两种不同的类型。 cast 表达式需要实例化两个重载的异常规范,以确定它们中的哪一个与 cast 的目标类型匹配(即应该选择它们中的哪一个),而 call 表达式只需要实例化异常规范重载决议选择的重载。
我相信 Clang 要求实例化函数模板特化的异常规范是正确的。 GCC首先检查 non-template 函数的类型是否匹配是合理的,如果匹配,则不需要检查函数模板特化的类型,因为知道即使匹配, tie-breaker 无论如何都会消除它。这也可能是符合标准的,这里的标准不明确。
我正在尝试将函数指针静态转换为特定函数重载,但似乎 clang 仍在解析(未使用的)模板特化的 noexcept 语句,因此生成编译器错误。 如果未使用相应的函数重载,GCC 似乎并不关心 noexcept。
template<typename T>
void fun( T ) noexcept( T(1) ){}
void fun(int) {}
void fun(int*) {}
int main () {
int a;
fun(&a); //calling works fine
fun(a);
static_cast<void(*)(int*)>(&fun); // static casting doesn't
}
哪个编译器错了?
当将函数指针转换为特定重载时,该标准是否具体规定了应该编译的内容?
编辑: 在 Maxim 的评论之后,我将第二个 noexcept 放回示例中,它在 gcc 和 clang 上编译。
所以这是另一个例子,它实际上失败了 noexcept(noexcept(...))
当谈到获取函数模板的地址时,正如您的静态转换所做的那样,标准有以下相关段落:
[over.over] Address of overloaded function (emphasis mine)
2 If the name is a function template, template argument deduction is done ([temp.deduct.funcaddr]), and if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization, which is added to the set of overloaded functions considered.
模板参数推导(不考虑异常规范),成功。那么整个函数就被实例化了。只有在这个实例化之后,编译器才会检查是否有更好的 non-template 匹配:
4 If more than one function is selected, any function template specializations in the set are eliminated if the set also contains a function that is not a function template specialization
这与函数调用的情况不同,在函数调用中,重载决议将根据 non-template 重载的存在提前放弃函数特化。在这种情况下,标准在确定确实需要它之前需要函数左值的存在。我不确定它是否可以被视为措辞缺陷,或者有更高的原因,但它看起来确实 un-intuitive.
所以结论是 Clang 没有错,但 GCC 的行为更直观。
有点off-topic,但值得一提的是,如果您想说函数模板 fun
只有在 T(1)
不抛出时才为 noexcept
,然后
template<typename T>
void fun( T ) noexcept( T(1) ){}
应该是
template<typename T>
void fun( T ) noexcept(noexcept(T(1))){}
[over.over] ¶1 A function with type
F
is selected for the function typeFT
of the target type required in the context ifF
(after possibly applying the function pointer conversion ([conv.fctptr])) is identical toFT
.[conv.fctptr] A prvalue of type "pointer to
noexcept
function" can be converted to a prvalue of type "pointer to function".
这意味着,在完成模板参数推导后 ([over.over] ¶2) 确定要添加到重载集中的模板函数特化,模板函数特化和 non-template 函数的类型都必须与目标类型进行比较,以决定应该“选择”它们中的哪一个。
tie-breaker 优先使用 non-template 函数 (¶4) 仅当两个函数的类型都匹配目标类型时才会生效(即如果选择了多个功能)。
void(*)(int*)
和 void(*)(int*)noexcept
是两种不同的类型。 cast 表达式需要实例化两个重载的异常规范,以确定它们中的哪一个与 cast 的目标类型匹配(即应该选择它们中的哪一个),而 call 表达式只需要实例化异常规范重载决议选择的重载。
我相信 Clang 要求实例化函数模板特化的异常规范是正确的。 GCC首先检查 non-template 函数的类型是否匹配是合理的,如果匹配,则不需要检查函数模板特化的类型,因为知道即使匹配, tie-breaker 无论如何都会消除它。这也可能是符合标准的,这里的标准不明确。