模板特化作为函数指针

时间:2017-02-06 14:42:41

标签: c++ c++11 templates visual-studio-2015

请考虑以下代码:

typedef void(__cdecl *logging_fnc)(int, const char*, ...);

template<logging_fnc LogFnc>
class Logger {
public:
  void foo()
  {
    LogFnc(1, "Foo");
  }

};

template<class Target, Target& instance>
void MakeLogAtTarget(int lvl, const char* msg...)
{
   instance.log("specific foo", lvl, msg);
}

Logger某处只是从某种方法调用非类型模板LogFnc

现在我还有一个单例类MyTarget

class MyTarget {
public:
  void log(const char* targetSpecific, int lvl, const char* msg)
  {
    printf("%s %s", targetSpecific, msg);
  }

  static MyTarget& GetInstance()
  {
    static MyTarget myInstance;
    return myInstance;
  }

private:
  MyTarget() = default;
};

为什么以下内容不起作用?

using TargetLogger = Logger<MakeLogAtTarget<MyTarget, MyTarget::GetInstance()>>;

我一直在:cannot convert from 'void (__cdecl *)(int,const char *,...)' to 'logging_fnc'

是否仅仅是因为编译器无法获得它生成的MakeLogAtTarget专门化的正确地址?

一般来说:是否可以向非模板参数提供指向函数的指针,该函数又调用某个类实例中的方法?

更新:也尝试了

static const MyTarget targetInstance = MyTarget::GetInstance();
using TargetLogger = Logger<MakeLogAtTarget<MyTarget, targetInstance)>>;

仍然是同样的错误。

更新

static const MyTarget targetInstance = MyTarget::GetInstance();
static const logging_fnc targetFnc = MakeLogAtTarget<MyTarget, targetInstance>;
using TargetLogger = Logger<targetFnc>;

在声明targetFnc的行输出相同的错误,而且在最后一行显示另一个错误: 'targetFnc': an expression involving objects with internal linkage cannot be used as a non-type argument

3 个答案:

答案 0 :(得分:3)

问题是您未能满足Target&类型的模板非类型参数的要求。来自[temp.arg.nontype]:

  

非类型模板参数 template-argument 应为 template-parameter <类型的转换常量表达式(5.20) / em>的

转换常量表达式禁止来自[expr.const]:

  

条件表达式 e是一个核心常量表达式,除非评估e,遵循抽象机器的规则(1.9 ),将评估以下表达式之一:[...]为文字类,constexpr函数或简单析构函数的隐式调用调用除constexpr构造函数之外的函数(12.4)

您需要一些特定的MyTarget对象来引用(或者在帖子N4198世界中,constexpr函数返回此类事物或类似内容。像:

struct MyTarget { ... };
static MyTarget x;

using TargetLogger = Logger<MakeLogAtTarget<MyTarget,x>>;

答案 1 :(得分:0)

问题可能会简化为简单的

template <typename T, T& t> void bar() {}

struct Z
{
  static constexpr Z& get();
};

static Z z;

constexpr Z& Z::get() { return z; }

int main()
{
    bar<Z, Z::get()>();
}

在C ++ 11和C ++ 14模式下,GCC以

响应
main.cpp: In function 'int main()':
main.cpp:14:22: error: no matching function for call to 'bar()'
     bar<Z, Z::get()>();
                      ^
main.cpp:1:34: note: candidate: template<class T, T& t> void bar()
 template <typename T, T& t> void bar() {}
                                  ^~~
main.cpp:1:34: note:   template argument deduction/substitution failed:
main.cpp:14:22: error: 'Z::get()' is not a valid template argument for type 'Z&' because it is not an object with linkage
     bar<Z, Z::get()>();
                      ^

它不希望您尝试使用函数return作为引用类型模板参数的参数。

一旦代码变得更复杂(与您的代码相似),描述性错误消息就会消失,替换为您引用的消息或非常类似的消息。

然而,它在-std=c++17模式下编译得很好。

答案 2 :(得分:0)

目前尚不清楚你想要实现的目标。您似乎有一个想要输入记录器的单例实例?我错过了更大的图景。

template<class Target, Target& instance>

这里似乎你想使用一个对象的实例作为模板参数。但是,afaik模板是编译时构造,而实例是在运行时生成的。在这种情况下,这种结构是不可能的。但我不是专家。

编辑:hmm,再次为-1反馈修饰符。这些人/ gal应该评论为什么。

编辑2: 所以现在我更了解你的问题。但问题是:是否需要让MakeLogAtTarget完全模板化?

您可以使用函数指针作为模板参数。 E.g:

template<class Target, Target& (*Getter)()>
void MakeLogAtTarget(int lvl, const char* msg...)
{
    Getter().log("specific foo", lvl, msg);
}

因此你可以使用:

来调用它
using TargetLogger = Logger < MakeLogAtTarget < MyTarget, MyTarget::GetInstance >> ;

这就是你要找的东西吗?