【发布时间】:2021-07-03 17:06:39
【问题描述】:
我想获得满足某个概念的所有完整类型。这些类型应列为std::tuple 的元素类型。
为:
#include <concepts>
#include <tuple>
class A{};
template<typename T>
concept InterestingType = std::derived_from<T, A>;
class B : public A{};
class C : public A{};
class D{};
我想获得std::tuple<A, B, C> 类型(因为A、B 和C 满足InterestingType 的概念,但D 不满足。)。元组中类的顺序无关紧要(例如std::tuple<C, B, A> 也可以)。重复也可以(例如std::tuple<A, B, C, A, A>)。
这可能吗?
更新:正如 cmets 中所问的,这里计划的目的是回答这个问题:
我有许多预定义的类,它们具有字符串标识符并提供解释字符串的解析构造函数,例如:
struct MyInt {
static constexpr char name[] = "MyInt";
int i_;
MyInt(std::string);
}
struct MyDouble {
static constexpr char name[] = "MyDouble";
double d_;
MyInt(std::string);
}
还有一个工厂方法:
std::any make(std::string type_name, std::string constructor_arg){
// generated code
if (type_name == MyInt::name)
return MyInt{constructor_arg};
else if (type_name == MyDouble::name)
return MyDouble{constructor_arg};
else
return {};
}
make 实际上调用了一个编译时生成的类似 switch-case 的模板函数,该函数将类型元组(即std::tuple<MyInt, MyDouble>)作为模板参数并生成与上面提供的实现等效的代码。
我目前手动定义注册类型的元组。
这对于库附带的预定义类型是可以的。
我正在努力解决的问题(以及为什么我问第一个问题)是用户如何提供其他类型并注册它们。
最后说明: 由于其他技术原因,虚拟构造函数没有选项。 注册的类型(如MyInt 和MyDouble)不能有其他虚拟方法技术原因。
更新 2:
我已经使用下面来自 HolyBlackCat 的答案的代码构建了一个工作示例。它在 clang >= 10 下编译得很好,但在所有 gcc 版本上都失败了。结帐https://godbolt.org/z/9xxbo79rr
知道我需要更改什么才能使用 gcc 构建吗?至少 gcc 11 应该能够编译它。
#include <iostream>
#include <any>
#include <string>
#include <map>
using func_ptr = std::any (*)(std::string);
using func_map = std::map<std::string, func_ptr>;
struct Factory {
inline static func_map &make_funcs() {
static func_map func_ptrs;
return func_ptrs;
}
};
template<typename T>
struct Base {
inline static const std::nullptr_t dummy = [] {
// This code will run for each derived class, when the program starts.
Factory::make_funcs()[T::name] = [](std::string s) -> std::any { return T{std::move(s)}; };
return nullptr;
}();
// Force `dummy` to be instantiated, even though it's unused.
static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};
struct MyInt : Base<MyInt> {
static constexpr char name[] = "MyInt";
int i_ = 5;
MyInt(std::string) {}
};
struct MyDouble : Base<MyDouble> {
static constexpr char name[] = "MyDouble";
double d_ = 4.0;
MyDouble(std::string) {}
};
int main() {
for (auto &[_, make] : Factory::make_funcs()) {
auto instance = make("a");
try {
any_cast<MyDouble>(instance);
std::cout << "is MyDouble" << std::endl;
} catch (...) {}
try {
any_cast<MyInt>(instance);
std::cout << "is MyInt" << std::endl;
} catch (...) {}
}
}
更新 3:
它的工作原理是使dummy 的 init 函数成为一个未在类中定义的单独函数。见https://godbolt.org/z/hnW58zc4s
#include <iostream>
#include <any>
#include <string>
#include <map>
using func_ptr = std::any (*)(std::string);
using func_map = std::map<std::string, func_ptr>;
struct Factory {
inline static func_map &make_funcs() {
static func_map func_ptrs;
return func_ptrs;
}
};
template<typename T>
struct Base {
inline static std::nullptr_t init();
inline static const std::nullptr_t dummy = init();
// Force `dummy` to be instantiated, even though it's unused.
static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};
template<typename T>
std::nullptr_t Base<T>::init() {
Factory::make_funcs()[T::name] = [](std::string s) -> std::any { return T{std::move(s)}; };
return nullptr;
}
struct MyInt : Base<MyInt> {
static constexpr char name[] = "MyInt";
int i_ = 5;
MyInt(std::string) {}
};
struct MyDouble : Base<MyDouble> {
static constexpr char name[] = "MyDouble";
double d_ = 4.0;
MyDouble(std::string) {}
};
int main() {
for (auto &[_, make] : Factory::make_funcs()) {
auto instance = make("a");
try {
any_cast<MyDouble>(instance);
std::cout << "is MyDouble" << std::endl;
} catch (...) {}
try {
any_cast<MyInt>(instance);
std::cout << "is MyInt" << std::endl;
} catch (...) {}
}
}
【问题讨论】:
-
不,不可能,至少在添加反射之前是不可能的。如果您告诉我们这是做什么用的,我们或许可以提出其他解决方案...
-
好的,谢谢。我想等待 Reflectios TS 成为标准的一部分可能需要很长时间。所以我更新了原始问题,详细说明了我需要类型元组的问题。
-
什么是“虚拟构造函数”?
-
好问题。我想那是无稽之谈。我想说的是 MyInt 和 MyDouble 不能有虚方法。
标签: c++ templates c++20 c++-concepts