在只读(即 const)访问器上执行结构化绑定的最佳实践是什么?

What is the best practice for performing structured bindings on read-only (i.e. const) accessors?

考虑以下 class foo:

class foo {
private:
    int x = 0;
    int y = 0;

public:
    template <std::size_t I>
    int const& get() const
    {
        if constexpr (I == 0) {
            return x;
        }
        else return y;
    }
};

我想为 foo 实现结构化绑定,所以我添加了以下内容:

namespace std {
    template <>
    struct tuple_size<foo> : std::integral_constant<std::size_t, 2u> {};

    template <std::size_t I>
    struct tuple_element<I, foo> {
        using type = int;
    };
}

现在,如果用户要执行结构绑定,he/she 必须显式添加 const 限定符:

int main() {
    foo f;
    auto [a, b] = f;            // Won't compile
    auto& [x, y] = f;           // Won't compile
    const auto [c_a, c_b] = f;  // Compiles
    const auto& [c_x, c_y] = f; // Compiles
    return 0;
}

(因为我们无法将 int& 绑定到 int const&

或者,我可以将 tuple_element typedef 设为 const int。现在所有四个语句都编译:

namespace std {
    template <>
    struct tuple_size<foo> : std::integral_constant<std::size_t, 2u> {};

    template <std::size_t I>
    struct tuple_element<I, foo> {
        using type = const int;         // <=== const added here
    };
}

int main() {
    foo f;
    auto [a, b] = f;            // Compiles
    auto& [x, y] = f;           // Compiles
    const auto [c_a, c_b] = f;  // Compiles
    const auto& [c_x, c_y] = f; // Compiles
    return 0;
}

现在请注意,即使它们的声明中没有 const 限定符,abxy 都是 int const .这可能会让用户感到困惑。

所以我的问题是:以上哪一项被认为是更好的做法?

So my question is: which one of the above is considered a better practice?

更喜欢第二种方法来尝试模仿内置和标准的情况:

class foo {
public:
    const int x = 0;
    const int y = 0;
};

int main() {
    foo f;
    auto [a, b] = f;            // Compiles
    auto& [x, y] = f;           // Compiles
    const auto [c_a, c_b] = f;  // Compiles
    const auto& [c_x, c_y] = f; // Compiles
}

binding_a_tuple-like_type

std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
const auto& [a,b,c] = tpl;
// a names a structured binding that refers to x; decltype(a) is float&
// b names a structured binding that refers to y; decltype(b) is char&&
// c names a structured binding that refers to the 3rd element of tpl; decltype(c) is const int