【问题标题】:Overloaded function templates that differ only in their return types in C++仅在 C++ 中的返回类型不同的重载函数模板
【发布时间】:2021-11-22 01:06:58
【问题描述】:

众所周知,只有返回类型不同的普通函数不能在 C++ 中重载。

但是这个限制不适用于重载的函数模板,例如:

int f(auto) { return 1; }
auto f(auto) { return 2; }

所有编译器都接受它,演示:https://gcc.godbolt.org/z/qj73Mzehd

为什么语言对模板有这样的例外?

如果重载函数的返回类型不同,则可以使用强制转换为预期函数类型来选择其中一个函数。令人惊讶的是,即使返回类型实际上相同,Clang 也允许人们解决歧义,例如:

((int(*)(int))f)(3);

选择

int f(auto) { return 1; }

演示:https://gcc.godbolt.org/z/snfvbq1ME

这里是 Clang 错了吗?

【问题讨论】:

  • gcc 不接受它:gcc.godbolt.org/z/j6Enr9h93,我不认为演员应该首先工作。有一些技巧可以将两个 auto 函数转换为模板以避免冲突(也许这些是由 clang 和 gcc 应用的),但它们仍然需要通过其他机制来消除歧义。如果您好奇,我可以将可能的模板显式代码放在答案中。
  • @Timo - 这无关紧要。关于签名的一点很重要。我希望这里的人们可以自己从谷壳中分拣出小麦。无论如何,我重新打开了,因为它不是关于专业化,而是模板本身。
  • 嗯,这与auto 参数无关。使用模板,函数定义也可以工作 gcc.godbolt.org/z/6dzc65xfa 。仍然 gcc 拒绝演员。
  • @StoryTeller-UnslanderMonica 是的,但是两种情况下的签名(包括模板参数)不一样吗?我认为自动返回类型不能作为模板参数。
  • @Timo - 在实例化主体时推断出,适用于每个专业化。模板一次也没有。

标签: c++ templates language-lawyer c++20 overload-resolution


【解决方案1】:

为什么语言对模板有这样的例外?

你是说这个吗?

签名 [defns.signature.templ]

⟨函数模板⟩名称、参数类型列表、封闭命名空间(如果有)、返回类型、模板头和尾随要求子句(如果有)

是的,返回类型在那里。这就是让事情成为可能的原因

template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type foo(T&);

template<typename T>
typename std::enable_if<!std::is_integral<T>::value>::type foo(T&);

SFINAE 是返回类型包含在签名中的原因。返回类型中可能出现替换失败,因此它是签名比较的一部分。您仍然可以生成两个相互冲突的特化来重载(在一般情况下,不在示例中),但模板会有所不同。

【讨论】:

  • SFINAE 是原因还是副作用?作为允许template &lt;typename T&gt; T Make();...
  • @Jarod42 - 对于这个特定的签名定义? SFINAE 我敢说。 C++98 没有这个,它只提到了专业化。
  • 我的意思是为什么仍然是一个有问题的问题。该模板例外允许 SFINAE 或 std::get&lt;0&gt;(my_tuple) 等多种内容。而且 C++98 已经允许两种用法和更多用法(std::tuple 不是 C++98 ^_^ 除外,但这种形式是允许的)。 (并且有不同的方法可以使用 SFINAE 而不将其作为默认参数;此外,返回类型 SFINAE 不适用于构造函数)。
  • @Jarod42 - 任何“为什么”的问题都是有问题的。总而言之,这个定义与许多其他关于改进 SFINAE 的工作一起出现。因此,说 SFIANE 是激励因素并非遥不可及。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多