c++ CRTP、容器和指针的问题

eblbsuwk  于 2023-01-06  发布在  其他
关注(0)|答案(1)|浏览(127)

我有一个CRTP * 基类 *:

template <class Child>
    class Container {
            friend Child;
            Container() {};
        public:
            decltype(auto) begin() { return static_cast<const Child&>(*this).abegin(); }
            decltype(auto) end() { return static_cast<Child&>(*this).aend(); }
            decltype(auto) begin() const { return static_cast<const Child&>(*this).abegin(); }
            decltype(auto) end() const { return static_cast<const Child&>(*this).aend(); }
};

现在,一个数组,试图成为那个容器的子容器:

template<typename T, size_t N>
    class Array: public Container<Array<T, N>> {
        public:
            T array[N];
        public:
            using iterator = zero::iterator::input_iter<T>;
            using const_iterator = zero::iterator::input_iter<const T>;

            // Iterator stuff
            iterator abegin() { return iterator(&array[0]); }
            iterator aend() { return iterator(&array[N]); }
            constexpr const_iterator abegin() const { return const_iterator(&array[0]); }
            constexpr const_iterator aend() const { return const_iterator(&array[N]); }

            /**
             * @brief returns the number of elements stored in the underlying array
             */
            [[nodiscard]]
            inline consteval int size() const noexcept { return N; }

            template <typename... InitValues>
            Array(InitValues... init_values) 
                : array{ init_values... } {}

    // code goes on...

下面是它的用法:

import std;
import zero;
import collections;
import iterator;
import container;
import type_info;

using namespace zero;

int main() {
    constexpr collections::Array a = collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L};
    Container b = collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L};

    std::cout << "Iterating over the values of a constexpr zero::collection!" << std::endl;
    std::cout << "decltype a: " << zero::types::type_name<decltype(a)>() << std::endl;
    
    for (long value : a)
        std::cout << " - [constexpr] Value: " << value << std::endl;

    std::cout << "Iterating over the values of a zero::container!" << std::endl;
    std::cout << "decltype a: " << zero::types::type_name<decltype(b)>() << std::endl;
    for (long value : b)
        std::cout << " - Value: " << value << std::endl;

    return 0;
}

结果是:

Iterating over the values of a constexpr zero::collection!
decltype a: const zero::collections::Array<long, 5>
 - [constexpr] Value: 1
 - [constexpr] Value: 2
 - [constexpr] Value: 3
 - [constexpr] Value: 4
 - [constexpr] Value: 5

Iterating over the values of a zero::container!
decltype a: zero::Container<zero::collections::Array<long, 5>>
 - Value: 8
 - Value: 0
 - Value: 1
 - Value: 2
 - Value: 3

好吧,我刚得了UB!
我误解了一些事情,很肯定,但我不能理解我在写什么,做什么坏事。

  • 我希望能够使用Container作为接口类型在类型定义,函数参数...典型的地方没有动态多态,只是使用CRTP

有人能发现我的错误并帮我改正吗?
编辑:这里是live example on Godbolt

bis0qfac

bis0qfac1#

UB的直接原因是 object slicingb的类型是Container<Array<long, 5>>-并且它是一个 complete 对象。周围没有Array<long, 5>可以作为基类的子对象。临时的collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L}不是一个。所以这个特定容器的成员函数内的每个强制转换都是UB。
为了修复它,需要将b作为Containerreference

const Container& b = collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L};

哎呀!这不能编译。你只能写Container的原因是CTAD,CTAD不适用于引用、指针或任何东西,除了类模板本身。Container * 不是类型 *,不能用作类型。它是一个模板。所以没有CTAD,你需要这样做:

const Container<collections::Array<long, 5>> & b = 
   collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L};

const,因为它是对临时变量的引用)
这看起来并不像Container那样吸引人,不是吗?如果你需要拼写collections::Array<long, 5>,你也可以这样写

const collections::Array<long, 5> & b = 
   collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L};

其更短并且包含同样多的关于b的信息。
现在,您为什么还需要Container呢?

相关问题