std :: pair <自动,自动>返回类型


16

我玩弄autostd::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
}

这适用于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<auto, auto>是一个int。如何解释呢?

海湾合作委员会10.0

error: returning initializer list

此错误是可以理解的,但是,我希望std::pair调用该构造函数,或者这里缺少什么?

lang9.0和10.0

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

好的,c不喜欢任何一个。从第二条错误消息中,似乎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};
    /* ... */
}

lang9.0和10.0

与以前相同,但有一个附加功能:

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

这里c仍然认为我们要返回一个int

gcc 9.2

和之前一样。

海湾合作委员会10.0

有用!

我猜某些功能仍必须实现,或者在上述一种情况下,是否有一个编译器正确而另一个错误?我认为示例2应该有效。还是不应该?

Answers:


23

语法:

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

是原始Concepts TS的一部分,但未包含在C ++ 20的Concepts提案中。因此,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<T, U>某些类型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<auto, auto>是一个int。如何解释呢?

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


对我来说,“ braised-init-list没有类型参数”是不清楚的。std :: pair <int,int> f(){return {1,2}; }确实有效,{1,2}没有任何类型(据我所知,它会调用std :: pair <int,int>的构造函数)。也许使用<auto,auto>,编译器无法推断出初始化程序列表{1,2}中1和2的类型?
mfnx

@mfnx Not没有类型参数,只是没有类型。大括号的初始化列表只能在某些情况下使用-例如初始化已知类型。但是它们不能用于演绎-因为它们没有类型。除外auto x = {1, 2};,但前提是所有类型都相同。
巴里

2
大多数编译器不只是在出现第一个错误时停止,而是尝试从中恢复,以便他们可以报告其他错误。这通常意味着假设所有不可解析的内容都是int。这不是int错误消息中的占位符;编译器确实认为这是一个int。(为了更清楚一点,gcc可能在某个时候会说“假设int”。)
Raymond Chen

2
请注意,扩展的另一种可能途径是允许返回类型的类模板参数推导,因为可行std::pair __f{1,2};
戴维斯鲱鱼

2
@DavisHerring我真的不想std::optional f() { return 4; }工作。
巴里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.