无法对用户定义类型使用 std::apply

Eri*_*ing 5 c++ tuples c++17 stdapply

在为我正在从事的某个项目实现一个compressed_tuple类时,我遇到了以下问题:我似乎无法将这种类型的实例传递给 std::apply,尽管根据以下内容这应该是可能的:https:// en.cppreference.com/w/cpp/utility/apply

我使用以下片段(godbolt)很容易地重现了这个问题:

#include <tuple>

struct Foo {
public:
    explicit Foo(int a) : a{ a } {}

    auto &get_a() const { return a; }
    auto &get_a() { return a; }

private:
    int a;
};

namespace std {

template<>
struct tuple_size<Foo> {
    constexpr static auto value = 1;
};

template<>
struct tuple_element<0, Foo> {
    using type = int;
};

template<size_t I>
constexpr auto get(Foo &t) -> int & {
    return t.get_a();
}

template<size_t I>
constexpr auto get(const Foo &t) -> const int & {
    return t.get_a();
}

template<size_t I>
constexpr auto get(Foo &&t) -> int && {
    return std::move(t.get_a());
}

template<size_t I>
constexpr auto get(const Foo &&t) -> const int && {
    return move(t.get_a());
}

} // namespace std

auto foo = Foo{ 1 };
auto f = [](int) { return 2; };

auto result = std::apply(f, foo);
Run Code Online (Sandbox Code Playgroud)

当我尝试编译这段代码时,似乎找不到std::get我定义的重载,即使它们应该完全匹配。相反,它尝试匹配所有其他重载(std::get(pair<T, U>)、std::get(array<...>) 等),而甚至不提及我的重载。我在所有三个主要编译器(MSVC、Clang、GCC)中都遇到一致的错误。

所以我的问题是这是否是预期的行为并且根本不可能std::apply与用户定义的类型一起使用?有解决方法吗?

康桓瑋*_*康桓瑋 8

\n

所以我的问题是这是否是预期的行为,并且根本不可能将 std::apply 与用户定义的类型一起使用?

\n
\n

不行,目前没有办法。

\n

在 libstdc++、libc++ 和 MSVC-STL 实现中,在内部std::apply使用std::get而不是 unqualified get,因为禁止用户get在 下定义namespace std,所以不可能应用于std::apply用户定义的类型。

\n

你可能会问,在[tuple.creation]中,标准是tuple_cat这样描述的:

\n
\n

[注 1:实现可以支持模板参数包中Tuples支持tuple类似协议的其他类型,例如pairarray。\xe2\x80\x94 尾注]

\n
\n

这是否表明其他tuple实用函数(例如)std::apply应该支持用户定义的tuple类似类型?

\n

请特别注意,术语“tuple类似”此时没有具体定义。这是 C++ 委员会有意留下的,以便通过未来的提案来填补这一空白。有一个提案将开始改善这个问题,请参阅P2165R3

\n
\n
\n

有解决方法吗?

\n
\n

不幸的是,在采用 P2165 之前,您可能必须实现自己的apply并使用不合格的get.

\n