【问题标题】:Templates, variadic function, universal references and function pointer : an explosive cocktail模板、可变参数函数、通用引用和函数指针:爆炸性鸡尾酒
【发布时间】:2013-02-08 11:38:38
【问题描述】:

考虑以下代码和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 没有采用int 的重载,这就是apply 试图与SameType 匹配的内容,如果我没记错的话。编辑:但将其更改为,例如double 也无济于事,显然是因为通用参考......
  • 老实说,我不明白为什么要限制函数的参数类型,或者为什么要将其限制为函数指针。愿意开导我吗?

标签: c++ templates c++11 function-pointers variadic-templates


【解决方案1】:

Several overloads exist of function std::sin,并且您的函数模板无法推断出会产生精确匹配的函数(在大多数情况下,推断模板参数时不考虑任何转换) .

首先,这些overlads 都不接受对他们的第一个(也是唯一一个)参数的引用。编译器告诉你这个,所以ReturnSameType不能从参数类型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&lt;int, 3&gt; 的实例上调用 apply()):不幸的是,没有任何现有的重载std::sin 具有 int 作为返回类型。

您可以尝试将您的实例化更改为 Array&lt;double, 3&gt;,但不幸的是,仅凭此无济于事,因为 C++11 标准的第 14.8.2/5 段规定:

非推断上下文是:

— 使用限定 ID 指定的类型的嵌套名称说明符。

— 非类型模板参数或绑定的数组,其中子表达式引用模板 参数。

- 用于具有默认实参的函数形参的形参类型中的模板形参 正在执行参数推导的调用中使用。

——一个函数参数,因为关联的函数,所以不能对其进行参数推导 参数是一个函数,或一组重载函数 (13.4),并且适用以下一项或多项:

——多个函数与函数参数类型匹配(导致二义性推导),或

——没有函数匹配函数参数类型,或者

作为参数提供的一组函数包含一个或多个函数模板

由于需要支持 integral 类型的重载(这是 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;
}

【讨论】:

    猜你喜欢
    • 2015-06-29
    • 2013-04-30
    • 1970-01-01
    • 2011-06-17
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 2014-12-31
    相关资源
    最近更新 更多