我正在编写一个类模板,它将任意函数指针作为非类型模板参数。我想用
template <auto F> struct Foo;
但我的编译器(MSVC 2017.5)在模板参数列表中不支持auto
(即使它支持许多C ++ 17功能)。所以我就这样写了一个黑客:
template <typename T>
using Func = void (*)(T);
template <typename TF, TF F> struct Foo;
template <typename T, Func<T> F>
struct Foo<Func<T>, F> { ... };
#define FOO(func) Foo<decltype(func), func>
我实现了一个流操作符(对于QDebug
或任何其他文本流),如下所示:
template <typename T, Func<T> F>
QDebug &operator <<(QDebug &out, const FOO(F) &foo)
{
...
return out;
}
但是我的主要代码找不到正确的operator<<
重载:
void func(int) { ... }
...
FOO(&func) foo;
qDebug() << foo; // error
令人惊讶的是,在定义像
这样的运算符时,一切都有效QDebug &operator <<(QDebug &out, const Foo<Func<T>, F> &foo)
// ^^^^^^^^^^^^^^^
似乎来自最后一行的Func<T>
和来自宏的decltype<F>
不提供相同的类型。但是我检查了这个并且std::is_same_v<Func<int>, decltype(&func)>
给出了真实。我想不出为什么使用宏FOO
给我任何其他编译时行为,就像没有使用它一样。
最小的工作示例:
#include <iostream>
template <typename T>
using Func = void (*)(T);
template <typename TF, TF F> struct Foo;
template <typename T, Func<T> F>
struct Foo<Func<T>, F> { };
#define FOO(func) Foo<decltype(func), func>
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const FOO(F) &foo)
// std::ostream &operator <<(std::ostream &out, const Foo<Func<T>,F> &foo)
{
return out;
}
void func(int);
int main(int argc, char **argv)
{
FOO(&func) foo;
std::cout << foo << std::endl; // error
}
答案 0 :(得分:2)
解决方法:
template <typename T, Func<T> F>
struct Foo<Func<T>, F> {
friend QDebug &operator <<(QDebug &out, const Foo &foo){
...
return out;
}
};
答案 1 :(得分:1)
作为template auto
paper的一部分,我们还在[temp.deduct.type]中获得了新的扣除规则:
当从表达式推导出与从属类型声明的非类型模板参数
P
对应的参数的值时,P
类型的模板参数是从价值的类型。
此规则允许以下示例在C ++ 17中工作,因为我们可以从T
的类型中推导出V
:
template <typename T, T V>
struct constant { };
template <typename T, T V>
void foo(constant<decltype(V), V> ) { }
int main() {
foo(constant<int, 4>{});
}
在C ++ 14及更早版本中,此示例格式错误,因为未推导出T
。当您使用该宏时,您正试图(隐式地)使用这种行为,该宏扩展为:
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const Foo<decltype(F), F> &foo);
您正试图从T
中推断F
。由于MSVC尚不支持template auto
,因此它也不足为奇,它也不支持机器的其他部分使template auto
工作。这就是宏观和非宏观替代方案之间的区别,它可以简单地推导出T
:
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const Foo<Func<T>,F> &foo)
所以简单的解决方案就是......不要使用宏,因为你有一个工作形式,即使它更冗长。更长的解决方案是使用Yakk's answer,它完全支持整个演绎问题。
答案 2 :(得分:0)
当没有使用宏时,可以从第二个函数参数F
推导出第二个模板参数foo
。使用宏时,无法从第二个函数参数推导出第二个模板参数F
,因为它将显示在decltype
:Foo<decltype(F), F> & foo
内。您的代码可以简化为
template<typename T>
void f(decltype(T) v){}
int v{};
f(v);
编译器知道参数的类型(int
)但是模板参数T
不能从已知的参数类型中推断出来,因为在decltype
T
内部使用时必须是提前知道。