【问题标题】:Visual Studio 2012 : no variadic templates : a solution?Visual Studio 2012:没有可变参数模板:解决方案?
【发布时间】:2013-12-14 14:18:59
【问题描述】:

我有一个类需要支持一组动态类型参数,但是 VS2012 不支持可变参数模板。 (VS2013 和编译器 CTP 支持可变参数模板,但我不能使用它们。我也不能使用 Boost。)

所以,我正在尝试通过使用“模板专业化”来找到解决方案。以下是我到目前为止所拥有的。 (请注意,如果您将 Signaler2 重命名为 Signaler...,则会出现一些编译问题。)

关于如何解决这个问题的任何想法?

#include <functional>
#include <vector>

using namespace std;

namespace spectralCore
{

#pragma region Signaler2<T1, T2>

// A signal object to handle signal/events notifications.
template<typename T1, typename T2>
class Signaler2
{
public:
  typedef std::function<void (T1,T2)> Func;

public:
  void Call(T1 arg1, T2 arg2)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)(arg1, arg2);

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)(arg1, arg2);
  }

  void operator ()(T1 arg1, T2 arg2)
  {
    Call(arg1, arg2);
  }

  Signaler2& operator*=(Func f)
  {
    _postHandlers.push_back( f );
    return *this;
  }

  Signaler2& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ( (*i).template target<void (T1,T2)>() == f.template target<void (T1,T2)>() )
      {
        _postHandlers.erase( i );
        break;
      }
    }

    return *this;
  }

  Signaler2& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler2& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (T1,T2)>() == f.template target<void (T1,T2)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ( (*i).template <void (T1,T2)>() == f.template target<void (T1,T2)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;
  std::vector<Func> _postHandlers;
};

#pragma endregion

#pragma region Signaler<T1>

// A signal object to handle signal/events notifications.
//template<typename T1> class Signaler<T1,void>
template<typename T1>
class Signaler
{
public:
  typedef std::function<void (T1)> Func;

public:
  void Call(T1 arg)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)( arg );

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)( arg );
  }

  void operator ()(T1 arg)
  {
    Call( arg );
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

  Signaler& operator*=(Func f)
  {
    _postHandlers.push_back( f );
    return *this;
  }

  Signaler& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
      {
        _postHandlers.erase( i );
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;     // First step handlers
  std::vector<Func> _postHandlers; // Second step handlers
};

#pragma endregion

#pragma region Signaler<void>

// A signal object to handle signal/events notifications.
template<>
class Signaler<void>
{
public:
  typedef std::function<void (void)> Func;

public:
  void Call()
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)();

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)();
  }

  void operator ()()
  {
    Call();
  }

  Signaler& operator*=(Func f)
  {
    _postHandlers.push_back( f );
    return *this;
  }

  Signaler& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ( (*i).template target<void (void)>() == f.template target<void (void)>() )
      {
        _postHandlers.erase( i );
        break;
      }
    }

    return *this;
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (void)>() == f.template target<void (void)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ( (*i).template target<void (void)>() == f.template target<void (void)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;     // First step handlers
  std::vector<Func> _postHandlers; // Second step handlers
};

#pragma endregion

}

注意到我也尝试了以下定义:

template<typename T1, typename T2=void> class Signaler
{ ... }

template<typename T1> class Signaler<T1,void>
{ ... }

template<> class Signaler<void,void>
{ ... }

但我收到“LINK”错误:

错误 LNK2001:未解析的外部符号“公共:静态类 Signaler ShadingSystem::Signal_ShaderUpdated”(?Signal_ShaderUpdated@ShadingSystem@2V?$Signaler@PEAVShader@@@spectralCore@@A) D:\spectralGraph.lib(VNScene.obj )

【问题讨论】:

  • 微软过去在 STL 中模仿可变参数模板的做法是多次包含相同的标头,其中一些宏定义不同,以便扩展为具有不同数量参数的模板。这很丑陋,但您也许可以使用相同的技巧。
  • 谢谢。当然这是一种方法。但首先我必须能够手动定义它......这是我现在尝试做的!一旦我编译了这段代码......我将能够处理宏;-)
  • 我不认为(*i).template target&lt;void (T1,T2)&gt;() == f.template target&lt;void (T1,T2)&gt;() 行做你认为它做的事。您无法检查 std::function 的使用方式是否相等 - 您需要单独的令牌。

标签: c++ templates visual-studio-2012 template-specialization variadic


【解决方案1】:

this question 看来,模板在 C++98 中的参数数量似乎无法重载。但是,您可以使用建议的技巧,将基本模板定义为具有支持的模板参数的最大数量,然后在尾部模板参数为空时进行特化。如果将此与重复的标头包含与宏扩展技巧相结合,您会得到如下内容:

Signaler.h:

#include <functional>
#include <vector>

template<typename T1 = void, typename T2 = void, typename T3 = void, typename T4 = void, typename T5 = void>
class Signaler
{
private:
  Signaler();
};

#define SIGNALER_EXPAND_TYPES(macro) 
#include "SignalerSpecialization.h"
#undef SIGNALER_EXPAND_TYPES

#define SIGNALER_EXPAND_TYPES(macro) macro(T1)
#include "SignalerSpecialization.h"
#undef SIGNALER_EXPAND_TYPES

#define SIGNALER_EXPAND_TYPES(macro) macro(T1), macro(T2)
#include "SignalerSpecialization.h"
#undef SIGNALER_EXPAND_TYPES

// Same for 3, 4 and 5 args

SignalerSpecialization.h:

#define SIGNALER_EXPAND_TYPE(T) T
#define SIGNALER_TYPE_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_TYPE)

#define SIGNALER_EXPAND_TEMPLATE_ARGUMENT(T) typename T
#define SIGNALER_TEMPLATE_ARGUMENT_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_TEMPLATE_ARGUMENT)

#define SIGNALER_EXPAND_PARAMETER(T) T arg##T
#define SIGNALER_PARAMETER_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_PARAMETER)

#define SIGNALER_EXPAND_ARGUMENT(T) arg##T
#define SIGNALER_ARGUMENT_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_ARGUMENT)

template<SIGNALER_TEMPLATE_ARGUMENT_LIST>
class Signaler<SIGNALER_TYPE_LIST>
{
private:
  typedef std::function<void (SIGNALER_TYPE_LIST)> Func;

public:
  void Call(SIGNALER_PARAMETER_LIST)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)(SIGNALER_ARGUMENT_LIST);

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)(SIGNALER_ARGUMENT_LIST);
  }

  void operator()(SIGNALER_PARAMETER_LIST)
  {
    Call(SIGNALER_ARGUMENT_LIST);
  }

  Signaler& operator*=(Func f)
  {
    _postHandlers.push_back(f);
    return *this;
  }

  Signaler& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ((*i).template target<Func>() == f.template target<Func>())
      {
        _postHandlers.erase(i);
        break;
      }
    }

    return *this;
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back(f);
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ((*i).template target<void (SIGNALER_TYPE_LIST)>() == f.template target<void (SIGNALER_TYPE_LIST)>())
      {
        _handlers.erase(i);
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ((*i).template target<void (SIGNALER_TYPE_LIST)>() == f.template target<void (SIGNALER_TYPE_LIST)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;     // First step handlers
  std::vector<Func> _postHandlers; // Second step handlers
};

#undef SIGNALER_EXPAND_TYPE
#undef SIGNALER_TYPE_LIST

#undef SIGNALER_EXPAND_TEMPLATE_ARGUMENT
#undef SIGNALER_TEMPLATE_ARGUMENT_LIST

#undef SIGNALER_EXPAND_PARAMETER
#undef SIGNALER_PARAMETER_LIST

#undef SIGNALER_EXPAND_ARGUMENT
#undef SIGNALER_ARGUMENT_LIST

PS:您重载 *= 和 /= 运算符的品味非常差。

【讨论】:

  • 您好,我尝试了另一种定义(请参阅问题),但出现 LINK 错误!有人有解决问题的想法吗?谢谢
  • 顺便说一句,Trillian,您的示例无法编译:'(
  • @Spectral 哎呀,它缺少 STL #includes,它现在应该可以工作了。至于您的链接错误,它与您的 ShadingSystem 类有关,而不是 Signaler 类。
  • 谢谢,但这是我得到的错误类型:“错误 C2248: 'Signaler::Signaler' : 无法访问在类 'Signaler' 中声明的私有成员”依赖属性.cpp”。该错误发生在 DependencyProperty 构造函数中。这是 DependencyProperty 类中信号变量的定义:“Signaler Signal_PropertyChanged;”。谢谢
猜你喜欢
  • 2013-11-01
  • 1970-01-01
  • 2013-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-26
  • 1970-01-01
  • 2015-11-28
相关资源
最近更新 更多