如何让C++编译器间接推导 T
How can I get the C++ compiler to deduce T indirectly?
我的模板很弱。我有这个代码:
template<typename T>
void Foo(void(*func)(T*)) { }
void Callback(int* data) { }
int Test()
{
Foo(Callback);
}
。但我想要比 C 讨厌的函数指针语法更具可读性的东西 void(*func)(T*)
.
我的团队中有人建议:
template<typename T>
struct Types
{
typedef void Func(T*);
};
template<typename T>
void Foo2(typename Types<T>::Func* func) {}
void Test2()
{
Foo2(Callback); // could not deduce template argument for 'T'
Foo2<int>(Callback); // ok
}
(我仍在争论这是否真的更具可读性,但这是一个单独的问题。
如何帮助编译器找出 T 是什么,而无需在调用方中显式指定它?
特征类从函数类型中提取T
。
template<class F>
struct CallbackTraits;
template<class T>
struct CallbackTraits<void(*)(T)>
{
typedef T ArgumentType;
};
您的示例可以像这样修改:
template<typename F>
void Foo(F func)
{
typedef typename CallbackTraits<F>::ArgumentType T;
}
void Callback(int* data) { }
int Test()
{
Foo(Callback);
}
此技术用于提升类型特征库:http://www.boost.org/doc/libs/1_57_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html
这篇博文详细介绍了该技术的实现:https://functionalcpp.wordpress.com/2013/08/05/function-traits/
不幸的是,这种方法隐藏了Foo
签名中有关传入参数约束的信息。在上面的示例中,参数必须是类型 void(T*)
的函数。
此替代语法与原始示例相同,但可读性略高:
template<typename T>
void Foo(void func(T*)) { }
另一种可能更具可读性的替代语法可以使用 c++11 的别名模板实现,如下所示:
template<typename T>
using Identity = T;
template<typename T>
void Foo(Identity<void(T*)> func) { }
不幸的是,最新的 MSVC 无法编译它,报告了内部编译器错误。
您将无法根据嵌套名称推断类型:没有理由为什么外部类型的不同实例不会定义相同的内部类型。不过,您可以使用using
别名:
template <typename T>
using Function = auto (*)(T*) -> void;
template <typename T>
void Foo(Function<T>) {
}
就个人而言,我建议不要使用其中任何一个:在实践中,实际采用一个函数对象似乎更可取,该对象稍后允许使用具有合适函数调用运算符的对象。对于回调,通常需要传入一些辅助数据。也就是说,您可以使用不受约束的模板或采用类型擦除类型的模板,具体取决于您要执行的操作:
template <typename Fun>
void Unconstrained(Fun fun) {
}
template <typename T>
void TypeErased(std::function<void(T*)> fun) {
}
不受约束的版本的优点是它可能会内联函数调用,但它的缺点是每个函数对象类型都会创建一个新的实例化,并且参数类型可能会有所不同。类型擦除版本实际上必须执行类似于虚拟函数调用的操作,但函数模板只有一个实例化(当然,每个参数类型T
(。
诚然,类型擦除版本的类型不会从函数指针(或任何其他不是std::function<void(X*)>
的参数(中推断出来,即您可能希望有一个转发函数
template <typename T>
void TypeErased(Function<T> fun) {
TypeErased(std::function<void(T)>(fun));
}
在 C++98 和 C++03 中,模板参数推导仅适用于函数(和方法(。
我不认为在最近的标准中情况发生了变化。
- C/C++编译器通常会删除重复的库吗
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- Win32编译器选项和内存分配
- MSVC多行宏编译器错误
- 静态数据成员的问题-修复链接错误会导致编译器错误
- C++,我收到一个无法理解的编译器错误
- 在线编译器中的分段C++没有打印消息
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- C/C++预处理器是否可以检测一些编译器选项
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 我需要知道编译器如何在cpp中使用析构函数
- 编译器如何区分std::vector的构造函数
- CLANG 编译器 说:变量"PTR"可能未初始化
- 告诉c++编译器该参数没有别名
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么所有C++编译器都会崩溃或挂起此代码
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 我收到同义重复编译器错误。我应该如何修复"类型"X"的参数与类型"X"的参数不兼容?
- 为什么C++编译器没有检测到正确声明的类?