请考虑以下代码:
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
答案 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 >> ;
这就是你要找的东西吗?