【问题标题】:Does this program violate the ODR?该程序是否违反了 ODR?
【发布时间】:2021-06-19 15:06:03
【问题描述】:

考虑这个程序:

#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>(); }

(Compiler Explorer)

各种版本的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 - 但如果该程序是格式错误的 NDR,我需要帮助理解为什么会这样。我在标准草案中检查了[temp.over.link],但我不确定我是否正确解释了它。据我了解,该程序没问题,因为这些函数模板具有不同的签名。

万一这个程序是正确的,为什么 MSVC 会拒绝它?

【问题讨论】:

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

标签: c++ visual-c++ language-lawyer


【解决方案1】:

MSVC 有一个错误。

对于大多数函数,参数等价就足以指示错误。对于模板,还涉及到返回类型表达式。

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

g&lt;T&gt;()/!g&lt;T&gt;() 部分可以拆分为 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() {}

它适用于所有 3 个编译器。

【讨论】:

  • 我认为这个答案应该改进。 “对于大多数函数,参数的等价性足以指示错误。”。这是模棱两可的。什么时候应该检测到错误?什么时候声明,或者什么时候重载决议失败?一旦您考虑模板,这就会变得相关。
猜你喜欢
  • 2016-10-20
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 2018-07-17
  • 2017-03-17
  • 2014-02-15
  • 2010-11-29
相关资源
最近更新 更多