【问题标题】:Are arrays covariant in their size?数组的大小是否协变?
【发布时间】:2011-06-11 12:40:52
【问题描述】:

有没有办法在数组大小中多态地使用新的std::array 类型?也就是说,如果我有表单的功能

void DoSomething(std::array<int, 5>& myArray) {
    /* ... */
}

那么,数学上是否明确定义了执行以下操作(即使它不是合法的 C++ 代码?)

std::array<int, 10> arr;
DoSomething(arr);

这在数学上是明确定义的,有没有办法编写std::array 使其数组元素是连续的并且这段代码可以编译?我能想到的唯一技术是有一些奇怪的模板元程序,其中std::array&lt;T, N+1&gt; 继承自std::array&lt;T, N&gt;,但我不认为这会强制数组元素是连续的。

【问题讨论】:

  • 如果你想要编译时多态性,为什么不直接在N 上模板化函数,或者如果你想要运行时多态性,只使用std::vector
  • @Karl Knechtel- 假设我想要一个函数,它总是接收“至少五个元素”或“至少十个元素”的数组。我可以使用模板来做到这一点,但这会导致代码重复,并且需要static_assert-esque 代码,这并不优雅。我不能用std::vector 静态检查这个。因此,让函数接受某个大小的数组的想法是尝试像 C99 静态大小的数组参数一样,可以在编译时检查。
  • 我不确定你的例子。具体来说,我不确定我是否理解“数学上定义明确”的意思。在DoSomething,如果你查询std::distance(myArray.begin(), myArray.end()),你期望的结果是什么? 510?
  • @James McNellis- 我在想“数学上定义明确”的意思是“在类型论的数学意义上,array 是 array 的子类型。”我希望它给出 10,因为这是数组的实际长度。
  • @templatetypedef: 如果它给出 10,那么它不是一个数组。如果 DoSomething 旨在处理其他大小的数组,那么它不应该专门将 array 作为参数。这是简单的 Liskov Substitution Principal 的东西......不要被搞砸了。

标签: c++ arrays c++11 polymorphism


【解决方案1】:

直接?没有。

但是,您可以使用编译时多态性来实现非常相似的功能,并且您可以编写一个引用包装器,使其更易于在代码中使用:

#include <array>
#include <cstddef>

template <typename T, std::size_t N>
struct ref_array_of_at_least
{
    template <std::size_t M>
    ref_array_of_at_least(T (&a)[M])
        : data_(a)
    {
        static_assert(M >= N, "Invalid size");
    }

    template <std::size_t M>
    ref_array_of_at_least(std::array<T, M>& a)
        : data_(&a[0])
    {
        static_assert(M >= N, "Invalid size");
    }

    T* data_;
};

用作:

void f(ref_array_of_at_least<int, 5>) { }

int main()
{
    std::array<int, 5> x;
    std::array<int, 6> y;
    std::array<int, 4> z;
    f(x); // ok
    f(y); // ok
    f(z); // fail
}

(您需要在ref_array_of_at_least 中添加一些operator[] 重载等,并且需要一些工作才能使其正确,但这是一个证明您正在寻找的可能性的开始。)

【讨论】:

    【解决方案2】:

    如果这是一个要求,一种方法是将运算符转换为所需类型:

    #include <iostream>
    
    template <typename T, int N>
    struct Array
    {
        Array() { for (int i = 0; i < N; ++i) x[i] = 0; }
    
        template <int N2>
        operator Array<T, N2>&()
        {
            // for safety, static assert that N2 < N...
            return reinterpret_cast<Array<T, N2>&>(*this);
        }
    
        int size() const { return N; }
        T x[N];
    
        friend std::ostream& operator<<(std::ostream& os, const Array& a)
        {
            os << "[ ";
            for (int i = 0; i < N; ++i) os << a.x[i] << ' ';
            return os << ']';
        }
    };
    
    
    void f(Array<int, 5>& a)
    {
        a.x[a.size() - 1] = -1;
    }
    
    int main()
    {
        Array<int, 10> a;
        std::cout << a << '\n';
        f(a);
        std::cout << a << '\n';
    }
    

    我不推荐它:太可怕了。更明确的机制似乎更不容易被滥用,而且更强大 - 有点像:

    template <size_t N2>
    Array<T,N2>& slice(size_t first_index)
    {
        return *(Array<T,N2>*)(data() + first_index);
    }
    
    // usage...
    f(a.slice<5>(3));  // elements 3,4,5,6,7.
    

    (清理铸件以获得加分:-/)

    【讨论】:

      【解决方案3】:

      不,但你可以伪造它:

      // Hide this function however you like: "detail" namespace, use "_detail"
      // in the name, etc.; since it's not part of the public interface.
      void f_detail(int size, int *data) {
        use(data, /* up to */ data + size);
      }
      
      int const f_min_len = 5;
      
      template<int N>
      void f(int (&data)[N]) {
        static_assert(N >= f_min_len);
        f_detail(N, data);
      }
      
      template<int N>
      void f(std::array<int, N> &data) {
        static_assert(N >= f_min_len);
        f_detail(N, &data[0]);
      }
      

      这是一个完整的示例,应该完全按照介绍的方式工作。您只需将数据类型从 int 更改(或使其成为模板参数)并根据需要添加 const。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-06
        • 1970-01-01
        • 2020-10-12
        • 1970-01-01
        • 1970-01-01
        • 2017-06-16
        • 1970-01-01
        • 2011-01-10
        相关资源
        最近更新 更多