我想创建一个应该存储实例的模板容器类
ABC
派生类。有一个限制,只有 ABC
派生类可以使用这个模板。
容器应该是派生类的静态成员。
这就是我现在所拥有的,尽管这行不通,因为
extendsABC
并不期待 shared_ptr
:
#include <list>
#include <memory>
#include <type_traits>
class ABC {
virtual void foo() = 0;
};
template <typename T>
concept extendsABC = std::is_base_of<ABC, T>::value;
template <extendsABC T>
struct extendsABCStore {
std::list<T> m_data;
};
struct Derived;
struct Derived : public ABC {
void foo() override{};
static extendsABCStore<std::shared_ptr<Derived>> instances;
};
构建输出:
<source>:22:10: error: constraints not satisfied for class template 'extendsABCStore' [with T = std::shared_ptr<Derrived>]
static extendsABCStore < std::shared_ptr < Derrived >> instances;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:13:12: note: because 'std::shared_ptr<Derrived>' does not satisfy 'extendsABC'
template < extendsABC T >
^
<source>:11:24: note: because 'std::is_base_of<ABC, shared_ptr<Derrived> >::value' evaluated to false
concept extendsABC = std::is_base_of<ABC, T>::value;
^
1 error generated.
Compiler returned: 1
你有几个问题。在
Derrived
的定义中,它是一个不完全类型,所以extendsABC < Derrived >
是false
。你还有一个指针,你想在指向的类型上进行约束。
为了解决第一个问题,我们可以使用静态成员函数而不是静态数据成员。
struct Derived : public ABC {
void foo() override{};
static auto& instances() {
static extendsABCStore<Derived> store;
return store;
}
};
要确定第二个,将
std::shared_ptr
放在extendsABCStore
的定义中
template <typename T>
struct extendsABCStore {
std::list<std::shared_ptr<T>> m_data;
};
这里的问题不在于定义概念。这可以做为
template<class T>
struct is_extendsABC : std::false_type {};
template<class T>
struct is_extendsABC<std::shared_ptr<T>> : std::bool_constant<std::is_base_of_v<ABC, T>> {};
template<typename T>
concept extendsABC = is_extendsABC<T>::value;
这里的问题是你使用的概念的类型(
Derrived
)在使用时是不完整的(instances
成员声明)。
您可以通过延迟类型检查直到
extendsABCStore
专门化来解决此问题:
template <class T>
struct extendsABCStore {
std::list<T> m_data;
static_assert(extendsABC<T>, "T needs to be a std::shared_ptr with an element type derived from ABC");
};
您需要编写一个采用类型
T
并返回: 的元函数
T
如果 T
不是 shared_ptr
shared_ptr
否则指向的类型。template<typename T>
struct remove_shared_ptr { using type = T; };
template<typename T>
struct remove_shared_ptr<std::shared_ptr<T>> { using type = T; };
template<typename T>
using remove_shared_ptr_t = typename remove_shared_ptr<T>::type;
// ...
template < typename T >
concept extendsABC = std::is_base_of<ABC, remove_shared_ptr_t<T> >::value;
我能理解错误:
std::shared_ptr<Derrived>
是指向 Derrived 类的智能指针。
您在
is_base_of trait
概念中使用的extendsABC
也需要类类型。
但是,我在这里看不到实用的解决方案。您尝试实施它的方式似乎是不可能的。尝试创建一个单独的容器类,而不将其绑定到任何特定的派生类。
因为概念不直接支持模板模板参数,您可以通过使用辅助类型特征引入额外的间接层来处理,如下所示:
#include <type_traits>
#include <list>
#include <memory>
class ABC {
public:
virtual void foo() = 0;
};
template <typename T>
concept extendsABC = std::is_base_of<ABC, T>::value;
template <typename T>
struct TypeWrapper {
using type = T;
};
template <typename T>
using TypeWrapper_t = typename TypeWrapper<T>::type;
template <typename T>
struct extendsABCStore {
std::list<T> m_data;
};
struct Derrived;
struct Derrived: public ABC {
void foo() override {};
static extendsABCStore<TypeWrapper_t<std::shared_ptr<Derrived>>> instances;
};
extendsABCStore<TypeWrapper_t<std::shared_ptr<Derrived>>> Derrived::instances;
int main() {
return 0;
}