【问题标题】:C++ metaprogramming issue/challenge to generate a type based on it function callsC++ 元编程问题/挑战基于它函数调用生成类型
【发布时间】:2025-12-02 10:00:02
【问题描述】:

我有一个 C++ 问题。我想根据传递给它的模板化函数的类型参数生成一个类型。

让我举例说明。

class A  {

   template<class B> M() { }

   void Z() {

   // NOTE: Here I want to call to X on each type that was feed it to M.
   X<N1>();   
   X<N1>();
   ...
   X<NN>();

   }

   template<class B> X() { }
   };

例如

   A a;

   a.M<int>();
   a.M<double>();

然后a.Z() 执行...

   X<int>();
   X<double>();

另一个考虑唯一类型的示例

   A a;

   a.M<int>();
   a.M<int>();
   a.M<double>();
   a.M<double>();

那么a.Z() 仍然会执行...

   X<int>();
   X<double>();

请注意,我是根据对 M 的调用生成类型 A。 好的!我认为对于那个 A 类,这在概念上是不可能的,因为 A 不是模板类型,然后它不能以这种方式变化,事实上,这对于 C++ 中的任何类型都是不可能的(我认为)。但我想让你明白。

我期待找到一种使用元编程来解决这个问题的方法,但欢迎任何建议、解决方案或参考。

【问题讨论】:

  • 你为什么要这样做?
  • @ecatmur 我们在创建某些对象时使用通用编程和某种优化,我们需要以非常干净的方式清理资源
  • 如果你想让a.Z()在编译时扩展,问题是语句a.M&lt;int&gt;();a.M&lt;double&gt;();在运行时是连续的,但在编译时完全不相关。 A&lt;int, double&gt; a; a.Z();A a; a.M&lt;int, double&gt;().Z(); 怎么样?
  • 这不能在编译时工作,因为在不同的翻译单元中可能有不同的实例化。如果将此限制为单个 TU,则可以使用 something like this
  • 为什么您认为以上方法可以很好地解决您的问题?在 C++11 中清理资源的干净方法是使用为您进行清理的 RAII 类型。看起来和上面的很不一样。

标签: c++ c++11 metaprogramming template-meta-programming


【解决方案1】:

无需元编程。

class A {
  using XPtr = void (A::*)();
  std::vector<XPtr> x_calls;
  std::set<std::type_index> x_types;

  template <typename B> void X() { ... }

public:
  template <typename B> void M() {
    bool is_new = x_types.insert(std::type_index(typeid(B))).second;
    if (is_new)
      x_calls.push_back(&A::X<B>);
    ...
  }

  void Z() {
    for (auto&& ptr : x_calls) {
      (this->*ptr)();
    }
  }
};

【讨论】:

  • 那行不通,例如,如果您调用 M 两次,您将在 Z 中生成 2 次调用。这是不希望的。我将更新问题以清除该问题。不过还是谢谢
  • 现在可以了。谢谢。但老实说,我不喜欢生成的代码。在接受你的之前,我会尝试找到另一个选择。但再次感谢。
【解决方案2】:

首先,我认为你的接口并不是真正的 MPL。要成为 MPL,您可以将其称为 typedef MyType mpl::vector&lt;int, double&gt;,然后找到一种方法为每种类型构建一个名为 X&lt;...&gt; 的类型。不过……

#include <iostream>
#include <typeinfo>
#include <vector>
#include <functional>
#include <algorithm>

using namespace std;

template< typename T>
void X() {
    cout<<typeid(T).name()<<endl;
}

struct A {
  vector< function<void(void)> > callbacks;
  void z() { 
    for( auto a : callbacks ) a();
  }

  template<typename T>
  void M() {
    callbacks.push_back( [](){ X<T>();} );
  }
};

int main() {
    A a;
    a.M<int>();
    a.M<double>();
    a.z();
    return 0;
}

做你想做的。

$ g++ --std=c++11 && ./a.out

d
咝咝咝

See it live

【讨论】:

    【解决方案3】:

    您可以使用boost::fusion::setboost::mpl 实现类似的功能。

    class A {
        struct functoid {
            template<typename T>
            void operator(T t)
            {
                 /* do something */
            }
        }
        template<class B> M() {
    
            boost::mpl::for_each<B>(functoid());
        }
    }
    
    A a;
    a.template M<boost::fusion::set<int, double, ...>>();
    

    但是,在这种情况下,您需要知道实际的类型,或者,在operator() 中注册一些回调。

    【讨论】:

    • 塞巴斯蒂安的回答更好。
    最近更新 更多