模板,可变参数函数,通用引用和函数指针:爆炸性的鸡尾酒
Templates, variadic function, universal references and function pointer : an explosive cocktail
请考虑以下代码和apply
函数:
// Include
#include <iostream>
#include <array>
#include <type_traits>
#include <sstream>
#include <string>
#include <cmath>
#include <algorithm>
// Just a small class to illustrate my question
template <typename Type, unsigned int Size>
class Array
{
// Class body (forget that, this is just for the example)
public:
template <class... Args> Array(const Args&... args) : _data({{args...}}) {;}
inline Type& operator[](unsigned int i) {return _data[i];}
inline const Type& operator[](unsigned int i) const {return _data[i];}
inline std::string str()
{
std::ostringstream oss;
for (unsigned int i = 0; i < Size; ++i)
oss<<((*this)[i])<<" ";
return oss.str();
}
protected:
std::array<Type, Size> _data;
// Apply declaration
public:
template <typename Return,
typename SameType,
class... Args,
class = typename std::enable_if<std::is_same<typename std::decay<SameType>::type, Type>::value>::type>
inline Array<Return, Size> apply(Return (*f)(SameType&&, Args&&...), const Array<Args, Size>&... args) const;
};
// Apply definition
template <typename Type, unsigned int Size>
template <typename Return, typename SameType, class... Args, class>
inline Array<Return, Size> Array<Type, Size>::
apply(Return (*f)(SameType&&, Args&&...), const Array<Args, Size>&... args) const
{
Array<Return, Size> result;
for (unsigned int i = 0; i < Size; ++i) {
result[i] = f((*this)[i], args[i]...);
}
return result;
}
// Example
int main(int argc, char *argv[])
{
Array<int, 3> x(1, 2, 3);
std::cout<<x.str()<<std::endl;
std::cout<<x.apply(std::sin).str()<<std::endl;
return 0;
}
编译失败,并显示:
universalref.cpp: In function ‘int main(int, char**)’:
universalref.cpp:45:32: erreur: no matching function for call to ‘Array<int, 3u>::apply(<unresolved overloaded function type>)’
universalref.cpp:45:32: note: candidate is:
universalref.cpp:24:200: note: template<class Return, class SameType, class ... Args, class> Array<Return, Size> Array::apply(Return (*)(SameType&&, Args&& ...), const Array<Args, Size>& ...) const [with Return = Return; SameType = SameType; Args = {Args ...}; <template-parameter-2-4> = <template-parameter-1-4>; Type = int; unsigned int Size = 3u]
universalref.cpp:24:200: note: template argument deduction/substitution failed:
universalref.cpp:45:32: note: mismatched types ‘SameType&&’ and ‘long double’
universalref.cpp:45:32: note: mismatched types ‘SameType&&’ and ‘float’
universalref.cpp:45:32: note: mismatched types ‘SameType&&’ and ‘double’
universalref.cpp:45:32: note: couldn't deduce template parameter ‘Return’
我不太确定为什么它会失败。在该代码中,我想要:
- 保持通用引用具有最通用的函数
- 能够使用语法
apply(std::sin)
那么我必须更改什么才能使其编译?
函数std::sin
存在多个重载,并且您的函数模板无法推断出一个会产生完全匹配的重载(在大多数情况下,在推导模板参数时不考虑任何转换(。
首先,这些霸道者中没有一个接受对他们的第一个(也是唯一的(论点的引用。编译器告诉你这一点,所以Return
和SameType
不能从参数的类型中推断出来f
:
universalref.cpp:24:200: note: template argument deduction/substitution failed:
universalref.cpp:45:32: note: mismatched types ‘SameType&&’ and ‘long double’
universalref.cpp:45:32: note: mismatched types ‘SameType&&’ and ‘float’
universalref.cpp:45:32: note: mismatched types ‘SameType&&’ and ‘double’
因此,第一步包括更改apply()
函数模板的签名:
[...] apply(Return (*f)(SameType, Args&&...), [...]
^^^^^^^^
No URef!
此外,SFINAE 正在消除函数模板的所有实例化,其中Return
未推断为int
(您正在Array<int, 3>
实例上调用apply()
(:不幸的是,std::sin
的现有重载都没有int
为返回类型。
您可以尝试将实例化更改为 Array<double, 3>
,但不幸的是,由于 C++11 标准第 14.8.2/5 段所述,仅此一项无济于事:
非推导上下文是:
— 使用限定 id 指定的类型的嵌套名称说明符。
— 非类型模板参数或子表达式引用模板的数组绑定 参数。
— 具有默认参数的函数参数的参数类型中使用的模板参数 这在正在进行参数推导的调用中使用。
— 无法对其进行参数推导的函数参数,因为关联的函数 参数是一个函数或一组重载函数 (13.4(,以下一项或多项适用:
— 多个函数与函数参数类型匹配(导致推导不明确(,或
— 没有函数与函数参数类型匹配,或者
— 作为参数提供的函数集包含一个或多个函数模板。
由于重载需要支持整型(这是 C+11 中的新功能,请参阅 26.8/11(,因此库实现很可能确实为 std::sin
定义了模板重载。这就是stdlibc++的定义
template<typename _Tp>
inline _GLIBCXX_CONSTEXPR
typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
sin(_Tp __x)
{ return __builtin_sin(__x); }
有很多方法可以离开这里,但你可能不喜欢它们。除了上述必要(但不充分(的更改外,还需要将std::sin
显式转换为所需的类型。我还将在此处删除 SFINAE 条件,因为它除了强制执行SameType
等于 Type
之外没有其他任何操作:
// Declaration in the `Array` class
template <typename Return, class... Args>
inline Array<Return, Size> apply(
Return (*f)(Type, Args&&...),
const Array<Args, Size>&... args
) const;
[...]
// Example
int main(int argc, char *argv[])
{
Array<double, 3> x(1, 2, 3);
// ^^^^^^
// NOTICE
// THIS
std::cout<<x.str()<<std::endl;
std::cout<<x.apply((double(*)(double))std::sin).str()<<std::endl; // OK
// ^^^^^^^^^^^^^^^^^^^
// NOTICE THIS
return 0;
}
- QMetaObject invokeMethod的基于函数指针的语法
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- c++r值引用应用于函数指针
- 模板函数指针和lambda
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 带有类的函数指针
- () 函子后面的括号,而不是函数指针?
- 全局作用域中函数指针的赋值
- 使用"Task"函数指针队列定义作业管理器
- 将成员函数指针作为参数传递给模板方法
- 如何创建对象函数指针C++映射?
- 匹配函数指针作为模板参数?
- 通过函数指针定义类范围之外的方法
- 存储在类中的函数指针
- C++从函数指针数组调用函数
- 将返回值存储在函数指针数组的指针中是如何工作的?
- 整数键映射到头文件中的成员函数指针
- 从类成员函数到类 C 函数指针的转换
- 如何将内联匿名函数分配给C++函数指针
- 将字符缓冲区强制转换为函数指针