使用C ++中的字符串从类名创建对象

时间:2022-02-01 22:27:51

I have a Base class:

我有一个基类:

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

I have multiple classes that inherit from 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
}

Same for childB, childC, etc

对于childB,childC等也是如此

I want to know if it's possible to create childA, childB or childC using a string. I heard about variadic tempaltes but I don't really understand how to use it.

我想知道是否可以使用字符串创建childA,childB或childC。我听说过可变的tempaltes,但我真的不明白如何使用它。

2 个解决方案

#1


0  

Variadic template is a template, which can take an arbitrary number of template arguments of any type. Both the functions could be variadic since dawn of C language (printf function, for example), then macros and now - templates.

Variadic模板是一个模板,可以采用任意类型的任意数量的模板参数。从C语言(例如printf函数)开始,然后是宏和现在 - 模板,这两个函数都可以是变量函数。

You can declare it like this:

您可以这样声明:

template<typename... Arguments> class Variadic;

then specialize it with any number of arguments, including zero:

然后用任意数量的参数来专门化它,包括零:

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

Then you may use the argument list, known as argument pack, like this:

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

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

Just as in case of variadic functions, the argument pack can be preceded by concrete arguments:

就像可变函数的情况一样,参数包之前可以有具体的参数:

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

There is classic example of variadic template in STL: std::tuple. Some compilers do not support this feature fully or do not support at all, and in their case tuple is implemented through metaprogramming and macro definitions. There is no direct way in C++ to select particular argument from the list, like it is possible with variadic functions. It's possible to use recursion to iterate through them in one direction:

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...); 
}

Usually iteration would rely on function overloading, or - if the function can simply pick one argument at a time - using a dumb expansion marker:

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

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

which can be used as follows:

可以使用如下:

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

  expand(42, "answer", true);

which will expand to something like:

这会扩展到:

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

The use of this "pass" function is necessary, since the expansion of the argument pack proceeds by separating the function call arguments by commas, which are not equivalent to the comma operator. some_function(args)...; will never work. Moreover, this above solution will only work when the return type of some_function is not void. Furthermore, the some_function calls will be executed in an unspecified order, because the order of evaluation of function arguments is undefined. To avoid the unspecified order, brace-enclosed initializer lists can be used, which guarantee strict left-to-right order of evaluation. To avoid the need for a not void return type, the comma operator can be used to always yield 1 in each expansion element.

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

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

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

The number of arguments in argument pack can be determined by sizeof...(args) expression.

参数包中的参数数量可以通过sizeof ...(args)表达式确定。

As of creating initializers that use calls name it is possible only if name is defined at time of writing the code. There stingizer operator # in preprocessor that can be used, e.g.

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

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

printstring( This a dumb idea );

will generate code (assuming that C++ automatically joins string literals):

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

printf("This a dumb idea \n")

You can declare something like this:

你可以声明这样的东西:

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

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

How would variadic macro definitions and variadic template interact? I'm not sure. Compiler I have at hand failed, but it isn't C++11. Try that, if interested.

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

There might be typeid operator supporeted, depending on compiler settings.

根据编译器设置,可能会支持typeid运算符。

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

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

std::type_info got method name(), but string it returns is implementation dependant: http://en.cppreference.com/w/cpp/types/type_info/name

std :: type_info得到方法名称(),但它返回的字符串是依赖于实现的:http://en.cppreference.com/w/cpp/types/type_info/name

#2


0  

In c++14 you could create some helper struct to determine each character of the string you pass at compile-time and to forward it to a type. However string you pass need to be stored in variable with linkage to let compiler to use it as a non-type template parameter:

在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]

#1


0  

Variadic template is a template, which can take an arbitrary number of template arguments of any type. Both the functions could be variadic since dawn of C language (printf function, for example), then macros and now - templates.

Variadic模板是一个模板,可以采用任意类型的任意数量的模板参数。从C语言(例如printf函数)开始,然后是宏和现在 - 模板,这两个函数都可以是变量函数。

You can declare it like this:

您可以这样声明:

template<typename... Arguments> class Variadic;

then specialize it with any number of arguments, including zero:

然后用任意数量的参数来专门化它,包括零:

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

Then you may use the argument list, known as argument pack, like this:

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

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

Just as in case of variadic functions, the argument pack can be preceded by concrete arguments:

就像可变函数的情况一样,参数包之前可以有具体的参数:

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

There is classic example of variadic template in STL: std::tuple. Some compilers do not support this feature fully or do not support at all, and in their case tuple is implemented through metaprogramming and macro definitions. There is no direct way in C++ to select particular argument from the list, like it is possible with variadic functions. It's possible to use recursion to iterate through them in one direction:

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...); 
}

Usually iteration would rely on function overloading, or - if the function can simply pick one argument at a time - using a dumb expansion marker:

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

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

which can be used as follows:

可以使用如下:

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

  expand(42, "answer", true);

which will expand to something like:

这会扩展到:

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

The use of this "pass" function is necessary, since the expansion of the argument pack proceeds by separating the function call arguments by commas, which are not equivalent to the comma operator. some_function(args)...; will never work. Moreover, this above solution will only work when the return type of some_function is not void. Furthermore, the some_function calls will be executed in an unspecified order, because the order of evaluation of function arguments is undefined. To avoid the unspecified order, brace-enclosed initializer lists can be used, which guarantee strict left-to-right order of evaluation. To avoid the need for a not void return type, the comma operator can be used to always yield 1 in each expansion element.

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

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

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

The number of arguments in argument pack can be determined by sizeof...(args) expression.

参数包中的参数数量可以通过sizeof ...(args)表达式确定。

As of creating initializers that use calls name it is possible only if name is defined at time of writing the code. There stingizer operator # in preprocessor that can be used, e.g.

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

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

printstring( This a dumb idea );

will generate code (assuming that C++ automatically joins string literals):

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

printf("This a dumb idea \n")

You can declare something like this:

你可以声明这样的东西:

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

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

How would variadic macro definitions and variadic template interact? I'm not sure. Compiler I have at hand failed, but it isn't C++11. Try that, if interested.

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

There might be typeid operator supporeted, depending on compiler settings.

根据编译器设置,可能会支持typeid运算符。

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

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

std::type_info got method name(), but string it returns is implementation dependant: http://en.cppreference.com/w/cpp/types/type_info/name

std :: type_info得到方法名称(),但它返回的字符串是依赖于实现的:http://en.cppreference.com/w/cpp/types/type_info/name

#2


0  

In c++14 you could create some helper struct to determine each character of the string you pass at compile-time and to forward it to a type. However string you pass need to be stored in variable with linkage to let compiler to use it as a non-type template parameter:

在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]