std::pair<auto, auto> 返回类型

MFn*_*Fnx 17 c++ constexpr auto std-pair c++17

我在玩autoin std::pair。在下面的代码中,函数f应该返回std::pair依赖于模板参数的类型。

一个工作示例:

例 1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}
Run Code Online (Sandbox Code Playgroud)

这适用于 gcc 9.2、gcc 10.0、clang 9.0 和 clang 10.0。

接下来,std::pair为了清楚起见,我想明确地将返回类型写为 a :

例2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

gcc 9.2/10.0 和 clang 9.0/10.0 都无法编译这个。

海湾合作委员会 9.2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return
Run Code Online (Sandbox Code Playgroud)

从最后一条错误消息来看,gcc 9.2 似乎认为这std::pair<auto, auto>是一个int. 这怎么解释?

海湾合作委员会 10.0

error: returning initializer list
Run Code Online (Sandbox Code Playgroud)

这个错误是可以理解的,但是,我希望构造函数std::pair被调用,或者我在这里遗漏了什么?

叮当 9.0 和 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'
Run Code Online (Sandbox Code Playgroud)

好吧,clang 不喜欢这些。从第二条错误信息来看,clang 似乎也认为返回类型是int.

最后,为了修复使用 gcc 10.0 编译时出现的错误,我决定std::pair明确返回一个:

例3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

叮当 9.0 和 10.0

和以前一样,但有一个额外的:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'
Run Code Online (Sandbox Code Playgroud)

这里 clang 仍然认为我们在返回一个int?

海湾合作委员会 9.2

和之前一样。

海湾合作委员会 10.0

有用!

我想某些功能仍然需要实现,或者在上述一种情况下,是否有一个正确的编译器和另一个错误的编译器?在我看来,示例 2 应该有效。或者不应该?

Bar*_*rry 23

语法:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

是原始概念 TS 的一部分,但未包含在作为 C++20 一部分的概念提案中。因此,C++20 中唯一的占位符类型是auto(及其变体,如auto**decltype(auto)、 和受约束的占位符(Concept auto及其变体)。这种嵌套的占位符类型非常有用,但不是 C++20 的一部分,因此函数声明是格式错误的。

现在,gcc 允许它,因为 gcc 实现了概念 TS,我猜他们决定保留这个功能。clang 从未实现过 TS,所以它没有。

无论哪种方式,这:

std::pair<auto, auto> f() { return {1, 2}; }
Run Code Online (Sandbox Code Playgroud)

永远是畸形的。语法的含义是我们推导出返回类型,然后要求它匹配pair<T, U>某些类型TU。我们基本上是在尝试调用发明的函数:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed
Run Code Online (Sandbox Code Playgroud)

但是您不能从中推断出类型{1, 2}- 花括号初始化列表没有类型。也许这是应该探索的事情(因为至少在这样的简单案例中很容易理解),但从未被允许。因此,无论哪种方式,拒绝它都是正确的。

最后:

gcc 9.2 似乎相信这std::pair<auto, auto>是一个int. 这怎么解释?

出于某种原因(可能是由于我们的 C 遗留的隐式int),当 gcc 无法识别或理解类型时,它只是int在错误消息中用作占位符。这非常令人困惑,因为显然是 gcc 提出的,int而不是源代码。但事情就是这样。

  • 大多数编译器不会仅仅在出现第一个错误时停止,而是尝试从中恢复,以便报告其他错误。这通常意味着假设所有不可解析的东西都是“int”。这并不是说“int”是错误消息中的占位符;而是说“int”是错误消息中的占位符。编译器确实认为它是一个“int”。(为了更清楚地说明这一点,gcc 可能应该在某个时候说“假设 int”。) (2认同)
  • 请注意,另一种可能的扩展途径是允许返回类型的类模板参数推导,因为“std::pair __f{1,2};”有效。 (2认同)
  • @DavisHerring 我真的不想有`std::可选 f() { return 4; }` 工作。 (2认同)