这个程序违反了ODR吗?

10

考虑以下代码:

#include <type_traits>

template <typename T> constexpr bool g() { return true; }
template <typename T> std::enable_if_t< g<T>()> f() {}
template <typename T> std::enable_if_t<!g<T>()> f() {}

int main() { f<int>(); }

(编译器资源浏览器)

这对于各个版本的GCC和Clang都可接受,但是它不能用于MSVC,由于编译错误导致无法编译,将会出现错误提示信息。

1>D:\x.cpp(5,49): error C2995: 'enable_if<0,void>::type f(void)': function template has already been defined
1>D:\x.cpp(4): message : see declaration of 'f'
1>D:\x.cpp(4,49): error C3861: 'f': identifier not found

第一个错误信息让我想到了ODR违规,但如果这个程序是I形式的NDR的话,我需要帮助理解为什么会这样。我已经查看了标准草案中的[temp.over.link],但我不确定自己是否正确地解释了它。据我理解,程序是可以的,因为这些函数模板具有不同的签名。

如果这个程序正确的话,为什么MSVC会拒绝它呢?


Why "unlikely"? - Jeff Garrett
@JeffGarrett “不太可能”,因为我对规范的这个角落一无所知,而且违反单一定义规则并不需要编译器发出诊断消息(这样的程序是不合法的,无需诊断)。所以要么是编译器有 bug,要么是 MSVC 是唯一抱怨我的错误代码的编译器。 :) - smiling_nameless
那么你最后的陈述没有传达出来,你使用了“unlikely”的不正确方式。从你的措辞来看,似乎你认为代码是错误的是可能的。只需说“*如果这个程序是错误的,...*”。 - cigien
“函数模板签名”的问题在于签名属于模板的实例化。如果实例化,两个模板都将具有相同的返回类型。然而,模板参数也属于签名,这就是代码正确的原因。对于任何T,只能实例化其中的一个模板。没有实例化,就没有签名。 - MSalters
@MSalters,那个语句很令人困惑,因为函数模板确实有一个签名,而两个模板实例化的返回类型相同并不能告诉你函数模板具有相同的签名,因为签名(可能)包括导致该类型的表达式。 - Jeff Garrett
1个回答

4

这里出现了一个MSVC的bug。

对于大多数函数而言,参数相等就足以指示错误。但对于模板而言,返回类型表达式也会被涉及到。

MSVC似乎忽略了返回类型表达式,并且生成了错误信息,好像它可以忽略它一样。

g<T>()/!g<T>() 可以分为 sizeof(T)==1sizeof(T)!=1 两个部分,以消除另一个混淆特性(其中一个实际上无法被实例化)。

试试这个:

template <typename T, std::enable_if_t<g<T>(),bool> =true> void f() {}
template <typename T, std::enable_if_t<!g<T>(),bool> =true> void f() {}

它将在所有三个编译器中正常工作。


我认为这个答案需要改进:“对于大多数函数,参数的等效性足以表示错误。” 这是模棱两可的。 错误应该在什么时候被检测到? 在声明时还是在重载解析失败时? 一旦考虑模板,这就变得相关了。 - MSalters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接