我有一个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
1条答案
按热度按时间bis0qfac1#
UB的直接原因是 object slicing。
b
的类型是Container<Array<long, 5>>
-并且它是一个 complete 对象。周围没有Array<long, 5>
可以作为基类的子对象。临时的collections::Array<long, 5>{1L, 2L, 3L, 4L, 5L}
不是一个。所以这个特定容器的成员函数内的每个强制转换都是UB。为了修复它,需要将
b
作为Container
的 reference。哎呀!这不能编译。你只能写
Container
的原因是CTAD,CTAD不适用于引用、指针或任何东西,除了类模板本身。Container
* 不是类型 *,不能用作类型。它是一个模板。所以没有CTAD,你需要这样做:(
const
,因为它是对临时变量的引用)这看起来并不像
Container
那样吸引人,不是吗?如果你需要拼写collections::Array<long, 5>
,你也可以这样写其更短并且包含同样多的关于
b
的信息。现在,您为什么还需要
Container
呢?