在模板化函数中将 std::vector 设置为本地会导致编译失败

Kar*_*yan 2 c++ templates language-lawyer c++17

我正在玩下面的代码。

#include <iostream>
#include <span>
#include <vector>

std::vector v{1,2,3,4,5,6};

template <template <typename, std::size_t> class S>
S<int, std::dynamic_extent> GetSpan()
{
    return v;
}

int main()
{
    auto x = GetSpan<std::span>();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

GCC 和 Clang 都接受了这一点。但是,如果我std::vector在函数中设置 local in GetSpan(),则 i. e

template <template <typename, std::size_t> class S>
S<int, std::dynamic_extent> GetSpan()
{
    std::vector v{1,2,3,4,5,6};
    return v;
}
Run Code Online (Sandbox Code Playgroud)

Clang 仍然接受该代码,但 gcc 拒绝它。哪个编译器就在这里,为什么?

Art*_*yer 5

您的代码无法在 C++23 模式下编译。这是因为返回变量时进行了简化的隐式移动。(参见:cpppreferenceP2266R3

    std::vector v{1,2,3,4,5,6};
    return v;
Run Code Online (Sandbox Code Playgroud)

是相同的

    std::vector v{1,2,3,4,5,6};
    return std::move(v);
Run Code Online (Sandbox Code Playgroud)

...并且 astd::span<int>不能从右值向量构造。

之前的行为是尝试移动,如果该移动不可能,它就会复制。

看起来 gcc 即使在 C++20 模式下也会进行简化的移动。

其中之一可以修复它:

    return (std::vector<int>&) v;
    return static_cast<std::vector<int>&>(v);  // (This seems like the recommended solution)
    return static_cast<decltype((v))>(v);
    return static_cast<decltype(v)&>(v);
    return std::identity{}(v);

    // These two may have slightly different behaviour depending on
    // the constructors of the return type, but work here
    return { v };
    return S<int, std::dynamic_extent>(v);
Run Code Online (Sandbox Code Playgroud)

  • 请记住,答案不仅适用于OP,也适用于所有人。说“这就是解决问题的方法”可能会让很多人认为我只需要这样做就可以摆脱讨厌的错误,一切都会好起来的。恕我直言,这些技术可以消除错误,但它们不会消除返回的跨度不可用的问题,因为它指向不再存在的对象。 (3认同)