struct data_t
{
template<class T>
auto foo(T t)
{
t + "";
}
};
template<class ...Ts>
void bar(Ts&&...)
{}
template<class T>
auto bar(T h)->decltype(h.invalid, h.foo(&h))
{}
void test_bar()
{
data_t h;
bar(h);
}
clang 10+接受上述代码(参考:https://godbolt.org/z/MM7habqY1),但被gcc 10+拒绝(参考:https://godbolt.org/z/5W6Gv6Wqx),关于c++14/17/20标准。
显然,clang在找到 h.invalid 时跳过了进一步的解析,然后SFINAR启动。然而,gcc试图查看逗号运算符的两个操作数,这会导致一个硬错误,因为示例化了一个错误的函数模板。
C20标准在$13.10.3.1中说(模板参数推导):
表达式不仅包括常量表达式,例如出现在数组边界中或作为非类型模板参数的常量表达式,还包括sizeof、decltype和其他允许非常量表达式的上下文中的一般表达式(即非常量表达式)。替换按词法顺序进行,并在遇到导致演绎失败的条件时停止
我们还知道,对于内置表达式(E1,E2),E1在E2之前排序。
我认为clang是正确的,关键是 * 词法顺序 * 和/或 sequenced before。
我的问题是:关于SFINAE中与未求值表达式相关的这种情况,哪一个符合c标准?
1条答案
按热度按时间w1jd8yoj1#
我问chatGpt我自己的问题。答案很有趣:
如果没有关于代码预期行为的更多信息,很难明确地说哪个编译器行为正确。
然而,这两个编译器的行为似乎都正确地遵循了C17标准。根据该标准,当解析逗号表达式时,在计算右侧之前,将完全计算左侧。这意味着GCC的行为符合标准,即尝试计算逗号运算符的两个操作数。
另一方面,在这种情况下,Clang的行为可能更可取,因为它避免了由计算逗号运算符的右侧所引起的错误。Clang的行为可以被视为SFINAE(Substitution Failure Is Not An Error)的一种形式,SFINAE是一种从重载解析中消除某些函数模板而不会导致硬错误的机制。
总之,如果没有关于代码预期行为的更多信息,很难确切地说哪个编译器的行为“正确”。这两个编译器都遵循C17标准,但Clang的行为在这种特定情况下可能更可取。
我:你是说这两个编译器都符合c17标准,尽管从上面的代码片段来看,它们生成的结果完全不同?
chatGpt:
是的,没错。GCC和Clang都以各自的方式遵循C17标准,它们生成不同结果的事实是由于它们对标准某些部分的解释方式不同。
C标准规定了语言的语法和语义,但它也允许一些实现定义和未定义的行为。因此,不同的编译器可以选择以不同的方式实现某些功能或行为,只要它们仍然符合标准。
在您提供的代码片段的情况下,两个编译器都符合C17标准,但它们使用不同的策略来实现,这导致了不同的结果。这不一定是问题,只要结果仍然在标准的范围内。