3

在为我正在处理的某个项目实现一个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);

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

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

4

1 回答 1

6

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

不,没有办法。

[tuple.apply]中,在内部std::apply使用std:get。由于禁止用户定义getunder namespace std,因此目前无法应用于std::apply用户定义的类型。

同样在libstdc++libc++MSVC-STLstd::apply的实现中,在内部使用而不是 non-qualified 。std::getget

您可能会问,在[tuple.creation]中,标准描述tuple_cat如下:

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

这是否表明其他tuple实用程序功能,例如std::apply应该支持tuple-like 类型?

请注意,目前“ tuple-like”一词还没有具体的定义。这是故意离开 C++ 委员会的,以使未来的提案填补这一空白。有一个提案将开始改进此问题,请参阅P2165R2


有解决方法吗?

当前的解决方法是重新实现您自己的std::apply并在内部使用非限定的get

于 2021-09-17T03:43:10.480 回答