【问题标题】:std::pair<auto, auto> return typestd::pair<auto, auto> 返回类型
【发布时间】:2020-04-22 00:25:40
【问题描述】:

我在std::pair 中玩auto。在下面的代码中,函数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
}

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

接下来,为了清楚起见,我想将返回类型显式写为std::pair:

示例 2

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

gcc 9.2/10.0 和 clang 9.0/10.0 编译失败。

gcc 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

从上一条错误消息来看,gcc 9.2 似乎认为std::pair&lt;auto, auto&gt; 是int。这怎么解释?

gcc 10.0

error: returning initializer list

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

clang 9.0 和 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

好的,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};
    /* ... */
}

clang 9.0 和 10.0

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

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

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

gcc 9.2

和以前一样。

gcc 10.0

有效!

我猜有些功能还需要实现,或者在上面描述的一种情况下,是否存在一个正确而另一个错误的编译器?在我看来,示例 2 应该有效。还是不应该?

【问题讨论】:

    标签: c++ c++17 constexpr auto std-pair


    【解决方案1】:

    语法:

    std::pair<auto, auto> f() { return std::pair(1, 2); }
    ~~~~~~~~~~~~~~~~~~~~~
    

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

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

    不管怎样,这个:

    std::pair<auto, auto> f() { return {1, 2}; }
    

    总是格式不正确。语法的意思是我们推导出返回类型,然后要求它匹配pair&lt;T, U&gt;,对于某些类型T和U。我们基本上是在尝试调用发明的函数:

    template <typename T, typename U>
    void __f(std::pair<T, U>);
    
    __f({1, 2}); // this must succeed
    

    但是你不能从{1, 2} 中推断出一个类型——一个花括号初始化列表没有类型。也许这是应该探索的东西(因为至少在这样的简单案例中很容易理解),但它从未被允许。所以不管怎样拒绝都是正确的。

    最后:

    gcc 9.2 似乎认为std::pair&lt;auto, auto&gt; 是int。这怎么解释?

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

    【讨论】:

    • 我不清楚“braced-init-list 没有类型参数”。 std::pair f() { 返回 {1,2}; } 确实有效,并且 {1,2} 没有类型(据我了解,它调用 std::pair 的构造函数)。可能使用 编译器无法在初始化列表 {1, 2} 中推断出 1 和 2 的类型?
    • @mfnx Not 没有类型 argument,只是没有类型。大括号初始化列表只能在某些情况下使用 - 例如初始化已知类型。但它们不能用于演绎——因为它们没有类型。除了auto x = {1, 2}; 有效,但前提是所有类型都相同。
    • 大多数编译器不会在第一个错误处停止,而是尝试从中恢复,以便报告其他错误。这通常意味着假设所有不可解析的东西都是int。并不是int 是错误信息中的占位符;编译器真的认为它是int。 (为了更清楚地说明这一点,gcc 可能应该在某个时候说“假设 int”。)
    • 请注意,另一种可能的扩展途径是允许对返回类型进行类模板参数推导,因为std::pair __f{1,2}; 有效。
    • @DavisHerring 我真的不想让std::optional f() { return 4; } 工作。
    猜你喜欢
    • 2017-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-27
    • 1970-01-01
    • 2017-04-03
    • 1970-01-01
    • 2021-11-18
    相关资源
    最近更新 更多