如何键入用于 const 对象的自定义 io 操纵器?

How to type custom io manipulators for use with const objects?

我正在尝试提升我为使用 std::vectors 而编写的简单 Array-io 操纵器。这是我一直在使用的旧签名:

template<typename T>
struct arr {
   const size_t size;
   T* values;
   arr(const size_t size, T* values) : size(size), values(values) {};

   friend std::ostream& operator<<(std::ostream& os, const arr<T>& array);

   friend std::istream& operator>>(std::istream& is, arr<T>& array);
};

现在我试着像这样提起它:

template<typename T>
struct arr {
   std::vector<T>& vec;
   arr(std::vector<T>& vec) : vec(vec) {};

   friend std::ostream& operator<<(std::ostream& os, const arr<T>& array);

   friend std::istream& operator>>(std::istream& is, arr<T>& array);
};

但是我面临以下问题:我想在 const 声明的成员方法中使用 << arr(member)。这当然不能编译:

error: binding reference of type ‘std::vector<std::unique_ptr<IController> >&’ to ‘const std::vector<std::unique_ptr<IController> >’ discards qualifiers

然而,当我将构造函数参数和 arr::vec 更改为 const std::vector<T>& 时,我遇到了相反的问题,>> arr(member) 无法再工作了!

我希望通过将 arr 实例初始化为 const 我可以解决这个问题,但我遇到了同样的编译器错误,下面一行:

const streamutils::arr<int> list(myVector);

如何在不为进出方向声明两种不同类型的情况下解决这个问题? 我试图查看 libstdc++ 源代码以了解它是如何为 std::quoted 完成的,但我无法弄清楚。

您可以不使用 T 而是使用 vector<T> 本身对操纵器进行参数化。这样你就不用担心你的向量是不是 const 了。同时创建一个辅助函数,它 returns 具有相应模板类型的 class 实例。

template<typename T>
using is_vector = std::is_same<T, std::vector<typename T::value_type, typename T::allocator_type>>;

template<typename T>
struct Arr {
    static_assert(is_vector<std::decay_t<T>>::value);

    T& vec;

    // Note that arr is passed by value here because it is a temporary
    // in expressions like 'cin >> arr(a)'
    template<typename U>
    friend std::enable_if_t<!std::is_const_v<U>, std::istream&> operator>>(std::istream& in, Arr<U> Arr);

    template<typename U>
    friend std::ostream& operator<<(std::ostream& out, const Arr<U>& Arr);
};

template<typename T>
std::enable_if_t<!std::is_const_v<T>, std::istream&> operator>>(std::istream& in, Arr<T> arr) {
    int n;
    in >> n;
    arr.vec.resize(n);
    for (int i = 0; i < n; ++i) {
        in >> arr.vec[i];
    }
    return in;
}

template<typename T>
std::ostream& operator<<(std::ostream& out, const Arr<T>& arr) {
    out << arr.vec.size() << "\n";
    for (const auto& x: arr.vec) {
        out << x << " ";
    }
    out << "\n";
    return out;
}

template<typename T, typename = typename is_vector<std::decay_t<T>>::type>
Arr<T> arr(T& t)
{
    return Arr<T>{t};
}

int main() {
    vector<int> a;
    cin >> arr(a);
    cout << arr(a) << endl;

    const vector<int> b{1, 2, 3};
    cin >> arr(b); // compile error
    cout << arr(b) << endl;
}

此外,请考虑阅读 this post,它解释了交友模板运算符的各种方法(我在这里展示的不是最好的,也不是唯一可能的)。