-Werror 是否会干扰模板正确性 and/or SFINAE?
Does -Werror interfere with template correctness and/or SFINAE?
我觉得这是一个愚蠢的问题,我想答案很简单 "No",尽管除了寻求您的帮助之外我不知道如何确定它...
-Werror
是否会影响模板的正确性(不确定什么是正确的术语,请参见下面的示例)and/or SFINAE?
考虑这个简单的人为示例:
template <typename T>
void foo() {
int a;
}
int main() {
//int a; // error: unused variable 'a' [-Werror=unused-variable]
}
取消注释 main
中的行会导致在使用 -Werror
编译时出错。我知道编译器应该为任何模板参数错误的模板生成错误,即使没有实例化也是如此。这里不是这种情况。当我实例化模板时,我只会在这里看到错误(当然这实际上只是一个警告)。
为什么我问这个问题:我总是习惯用 -Werror
编译,因此我对什么是警告和什么是错误的理解在某些方面有点模糊。现在对于模板,尤其是 SFINAE,如果某些东西只是警告或真的是错误,它确实会产生很大的不同。
I know the compiler is supposed to generate an error for templates that are erroneous for any template parameter even if not instantiated.
但事实并非如此。如果无法为模板生成实例化,则程序格式错误,不需要诊断(1)。因此,无论您遇到错误还是编译,该程序都是格式错误的 "successfully."
从另一个角度来看,编译器不能让警告转换为错误影响 SFINAE,因为这可能会改变有效程序的语义,从而使编译器不符合要求。因此,如果编译器想要将警告诊断为错误,则必须通过停止编译而不是通过引入替换失败来实现。
换句话说,-Werror
可以使编译器拒绝一个格式良好的程序(毕竟这是它的预期目的),但如果它改变了一个程序的语义,那将是一个编译器错误。
(1) 引用 C++17 (N4659),[temp.res] 17.6/8:
The program is
ill-formed, no diagnostic required, if:
- no valid specialization can be generated for a template ... and the template is not instantiated, or
- ...
虽然这在很大程度上是实施质量问题,但 -Werror
确实(并且确实)会干扰 SFINAE。这是一个更复杂的例子来测试它:
#include <type_traits>
template <typename T>
constexpr bool foo() {
if (false) {
T a;
}
return false;
}
template<typename T, typename = void> struct Check {};
template<typename T> struct Check<T, std::enable_if_t<foo<T>()>> {};
int main() {
Check<int> c;
}
行 T a;
可以触发该警告(和错误),即使分支已死(它是故意死的,因此 foo
是一个 constexpr 函数,主要与 [=14 无关=]).现在,根据标准本身,这是一个格式良好的程序。
但是因为 Clang and GCC 在那里导致错误,并且该错误在 Check
专业化的非直接上下文中,我们得到了一个硬错误。即使根据标准本身,由于仅在直接上下文中替换失败,这应该回退到主模板。
我觉得这是一个愚蠢的问题,我想答案很简单 "No",尽管除了寻求您的帮助之外我不知道如何确定它...
-Werror
是否会影响模板的正确性(不确定什么是正确的术语,请参见下面的示例)and/or SFINAE?
考虑这个简单的人为示例:
template <typename T>
void foo() {
int a;
}
int main() {
//int a; // error: unused variable 'a' [-Werror=unused-variable]
}
取消注释 main
中的行会导致在使用 -Werror
编译时出错。我知道编译器应该为任何模板参数错误的模板生成错误,即使没有实例化也是如此。这里不是这种情况。当我实例化模板时,我只会在这里看到错误(当然这实际上只是一个警告)。
为什么我问这个问题:我总是习惯用 -Werror
编译,因此我对什么是警告和什么是错误的理解在某些方面有点模糊。现在对于模板,尤其是 SFINAE,如果某些东西只是警告或真的是错误,它确实会产生很大的不同。
I know the compiler is supposed to generate an error for templates that are erroneous for any template parameter even if not instantiated.
但事实并非如此。如果无法为模板生成实例化,则程序格式错误,不需要诊断(1)。因此,无论您遇到错误还是编译,该程序都是格式错误的 "successfully."
从另一个角度来看,编译器不能让警告转换为错误影响 SFINAE,因为这可能会改变有效程序的语义,从而使编译器不符合要求。因此,如果编译器想要将警告诊断为错误,则必须通过停止编译而不是通过引入替换失败来实现。
换句话说,-Werror
可以使编译器拒绝一个格式良好的程序(毕竟这是它的预期目的),但如果它改变了一个程序的语义,那将是一个编译器错误。
(1) 引用 C++17 (N4659),[temp.res] 17.6/8:
The program is ill-formed, no diagnostic required, if:
- no valid specialization can be generated for a template ... and the template is not instantiated, or
- ...
虽然这在很大程度上是实施质量问题,但 -Werror
确实(并且确实)会干扰 SFINAE。这是一个更复杂的例子来测试它:
#include <type_traits>
template <typename T>
constexpr bool foo() {
if (false) {
T a;
}
return false;
}
template<typename T, typename = void> struct Check {};
template<typename T> struct Check<T, std::enable_if_t<foo<T>()>> {};
int main() {
Check<int> c;
}
行 T a;
可以触发该警告(和错误),即使分支已死(它是故意死的,因此 foo
是一个 constexpr 函数,主要与 [=14 无关=]).现在,根据标准本身,这是一个格式良好的程序。
但是因为 Clang and GCC 在那里导致错误,并且该错误在 Check
专业化的非直接上下文中,我们得到了一个硬错误。即使根据标准本身,由于仅在直接上下文中替换失败,这应该回退到主模板。