代码之家  ›  专栏  ›  技术社区  ›  s-ol

如何键入自定义IO操纵器以用于常量对象?

  •  1
  • s-ol  · 技术社区  · 6 年前

    我正试图举起一个简单的数组IO操作器 std::vector 这是我用过的旧签名:

    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);
    };
    

    但是,我面临以下问题:我想使用 << arr(member) 从内部A const 声明的成员方法。当然,这不会编译:

    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 streamutils::arr<int> list(myVector);
    

    我如何解决这个问题,而不声明两种不同类型的进出方向? 我试图查看libstdc++源代码,以了解它是如何完成的。 std::quoted 但我想不出来。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Ivan Smirnov    6 年前

    可以参数化操纵器,而不是 T 但是有 vector<T> 本身。这样你就不必为向量的常量而烦恼了。还创建一个助手函数,该函数用相应的模板类型返回类的实例。

    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,它解释了各种让朋友成为模板操作员的方法(我在这里展示的不是最好的,也不是唯一可能的)。