繁体   English   中英

模板专门化为函数指针

[英]Template specialization as a function pointer

考虑以下代码:

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

还是一样的错误。

UPDATE

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

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

模板参数的用于非类型模板参数应为模板参数的类型的一个转换后的常量表达式(5.20)。

在转换的常量表达式禁止的地方,来自[expr.const]:

条件表达式 e核心常量表达式,除非遵循抽象机(1.9)的规则对e求值将对以下表达式之一求值:调用除constexpr构造函数之外的函数用于文字类, constexpr函数或对平凡析构函数的隐式调用(12.4)

您将需要引用一些特定的MyTarget对象(或者在N4198之后的世界中,使用constexpr函数返回此类内容或类似内容)。 喜欢:

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

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

该问题可能可以简化为一个简单的问题

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模式下很好地编译。

目前尚不清楚您要实现的目标。 似乎您有一个单例实例想要馈给记录器? 我想念大局了。

template<class Target, Target& instance>

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

编辑:嗯,再次是-1反馈修饰符。 这样做的家伙/女孩应该评论原因。

编辑2:现在,我更好地了解了您的问题。 但是,问题又来了:是否要求MakeLogAtTarget完全可模板化?

您可以做的是使用函数指针作为模板参数。 例如:

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 >> ;

那是您要找的东西吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM