【问题标题】:Create object from class name using a string in C++使用 C++ 中的字符串从类名创建对象
【发布时间】:2017-05-02 11:39:21
【问题描述】:

我有一个Base 类:

class Base() {
public:
   Base(int, int);
   ~Base();
};

我有多个继承自 Base 的类:

class childA : public Base {
public:
  childA(int, int, string);
  ~childA();
};

childA::childA(int x, int y, string str) : Base (x, y)
{
// do something here
}

childBchildC 等也一样

我想知道是否可以使用字符串创建childAchildBchildC。我听说过可变参数模板,但我不太了解如何使用它。

【问题讨论】:

  • 编译时字符串还是运行时字符串?
  • 工厂方法正是这样做的。
  • 告诉我更多关于@seccpur的信息
  • 使用字符串是什么意思?
  • @LorenceHernandez 我想要一种使用我的班级名称的字符串创建对象的方法

标签: c++ templates object inheritance


【解决方案1】:

可变参数模板是一个模板,它可以接受任意数量的任意类型的模板参数。自 C 语言诞生以来,这两个函数都可以是可变参数(例如 printf 函数),然后是宏,现在是模板。

你可以这样声明:

template<typename... Arguments> class Variadic;

然后用任意数量的参数对其进行专门化,包括零:

Variadic<double> instance;
Variadic<double, std::string> instance;
Variadic<> instance;

然后你可以使用参数列表,称为参数包,如下所示:

template<typename... Arguments> void SampleFunction(Arguments... parameters);

就像可变参数函数一样,参数包前面可以有具体参数:

template<typename First, typename... Rest> class BunchOfValues;

在 STL 中有一个经典的可变参数模板示例:std::tuple。一些编译器不完全支持或根本不支持此功能,在他们的情况下,元组是通过元编程和宏定义实现的。 C++ 中没有直接的方法从列表中选择特定参数,就像使用可变参数函数一样。可以使用递归在一个方向上遍历它们:

template<typename T> foo(T first)
{
    // do something;
}

template<typename T, typename U, typename ... Args> foo(T first, U second, Args... Rest)
{
    // do something with T
    foo(second, Rest...); 
}

通常迭代将依赖于函数重载,或者 - 如果函数一次只能选择一个参数 - 使用哑扩展标记:

template<typename... Args> inline void pass(Args&&...) {}

可以这样使用:

  template<typename... Args> inline void expand(Args&&... args) {
    pass( some_function(args)... );
  }

  expand(42, "answer", true);

这将扩展为:

 pass( some_function(arg1), some_function(arg2), some_function(arg3) etc... );

使用这个“pass”函数是必要的,因为参数包的扩展是通过逗号分隔函数调用参数来进行的,这不等同于逗号运算符。 some_function(args)...; 永远不会工作。此外,上述解决方案仅在 some_function 的返回类型不为 void 时才有效。此外, some_function 调用将以未指定的顺序执行,因为函数参数的评估顺序未定义。为了避免未指定的顺序,可以使用大括号括起来的初始化列表,这保证了严格的从左到右的评估顺序。为避免需要非 void 返回类型,逗号运算符可用于在每个扩展元素中始终产生 1。

  struct pass {
    template<typename ...T> pass(T...) {}
  };

  pass{(some_function(args), 1)...};

参数包中的参数个数可以由sizeof...(args)表达式确定。

在创建使用调用 name 的初始化程序时,只有在编写代码时定义 name 才有可能。预处理器中有stingizer operator#可以使用,例如

#define printstring( x ) printf(#x "\n")

printstring( This a dumb idea );

将生成代码(假设 C++ 自动加入字符串文字):

printf("This a dumb idea \n")

你可以这样声明:

template<typename T> class moniker
{
public:
    moniker(const char* tname);
}

#define declare_moniker(type, name)  moniker<type> name(#type)

可变参数宏定义和可变参数模板如何交互?我不知道。我手头的编译器失败了,但它不是 C++11。有兴趣可以试试。

可能支持 typeid 运算符,具体取决于编译器设置。

const std::type_info& ti1 = typeid(A);

std::type_info 获得了方法名(),但它返回的字符串取决于实现:http://en.cppreference.com/w/cpp/types/type_info/name

【讨论】:

  • 对于这个解释我比以前更理解了
  • 现在是否可以快速向我解释如何使用字符串和可变参数模板创建对象,如果这是正确的方法
  • @Rifzy 我不确定您要实现什么,为什么需要类名(在模板的情况下,从技术上讲,这是一个带有所有参数的长字符串)以及哪种组合与可变参数模板一起使用。您可以尝试使用 typeid(this).name()?
  • 我在做游戏
  • @Rifzy 你需要做什么?要确定您的对象在运行时是什么类型?有一些模式可以在不存储字符串的情况下实现这一点
【解决方案2】:

在 c++14 中,您可以创建一些辅助结构来确定您在编译时传递的字符串的每个字符并将其转发给一个类型。但是,您传递的字符串 需要 存储在带有链接的变量中,以让编译器将其用作非类型模板参数:

#include <utility>
#include <type_traits>

template <char... Cs>
struct string_literal { };

template <class T, T &, class>
struct make_string_literal_impl;

template <class T, T &Cs, std::size_t... Is>
struct make_string_literal_impl<T, Cs, std::index_sequence<Is...>> {
    using type = string_literal<Cs[Is]...>;
};

template <class T, T &>
struct make_string_literal;

template <class T, std::size_t N, T (&Cs)[N]>
struct make_string_literal<T[N], Cs>: make_string_literal_impl<T[N], Cs, std::make_index_sequence<N>> {
};

struct Base {
   Base(int, int) { }
   ~Base() { }
};

template <class>
struct Child: Base { 
    using Base::Base;
};

constexpr char const str[] = "abc";

int main() {
    Child<make_string_literal<decltype(str), str>::type> c(1, 1);
}

[live demo]

【讨论】:

    猜你喜欢
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    • 1970-01-01
    • 2015-09-08
    • 2019-03-20
    • 1970-01-01
    相关资源
    最近更新 更多