【问题标题】:Template function with std::async in clangclang中带有std :: async的模板函数
【发布时间】:2014-09-30 21:02:22
【问题描述】:

我在看std::asynchere的例子,如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>

template <typename RAIter>
int parallel_sum(RAIter beg, RAIter end)
{
    auto len = std::distance(beg, end);
    if(len < 1000)
        return std::accumulate(beg, end, 0);

    RAIter mid = beg + len/2;
    auto handle = std::async(std::launch::async,
                              parallel_sum<RAIter>, mid, end);
    int sum = parallel_sum(beg, mid);
    return sum + handle.get();
}

int main()
{
    std::vector<int> v(10000, 1);
    std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
}

我尝试使用 Clang 3.4 的 Web 编译器对其进行编译,结果输出 The sum is 而不是预期的 The sum is 1000

我复制了示例并使用以下命令在 Ubuntu 14.04.1 64 位上使用 clang 3.5-1ubuntu1 / gcc 4.8 编译:

clang++ -g main.cpp -std=c++1y -o out -pthread;

我收到以下错误:

main.cpp:15:19: error: no matching function for call to 'async'

    auto handle = std::async(std::launch::async,
                  ^~~~~~~~~~
main.cpp:24:35: note: in instantiation of function template specialization
      'parallel_sum<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> >
      > >' requested here
    std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
                                  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/future:1523:5: note: 
      candidate template ignored: substitution failure [with _Fn = int
      (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >,
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >), _Args =
      <__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > &,
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > &>]:
      function cannot return function type 'int (__gnu_cxx::__normal_iterator<int *,
      std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int *,
      std::vector<int, std::allocator<int> > >)'
    async(launch __policy, _Fn&& __fn, _Args&&... __args)
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/future:1543:5: note: 
      candidate template ignored: substitution failure [with _Fn = std::launch, _Args = <int
      (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >,
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >),
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > &,
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > &>]: no
      type named 'type' in 'std::result_of<std::launch (int
      (*)(__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >,
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >),
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > &,
      __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > &)>'
    async(_Fn&& __fn, _Args&&... __args)
    ^
1 error generated.
make: *** [all] Error 1

这是 clang、gcc、libstdc++ 中的错误,还是我遗漏了什么?

【问题讨论】:

  • 嗯。罪魁祸首在这里:with _Fn = int (__gnu_cxx::__normal_iterator&lt;..&gt;, __gnu_cxx::__normal_iterator&lt;..&gt;) 这应该是一个左值引用。好像扣法有问题。
  • 好的,我把它归结为以下问题:template&lt;class T&gt; void deduce(T&amp;&amp;) { /* print type T */ }。对于deduce(parallel_sum&lt;int*&gt;);,clang++ 推导出T == int(int*,int*),而g++ 推导出T == int(&amp;)(int*,int*)。这会导致问题。
  • 这看起来很像一个 clang++ 错误。如果函数不是函数模板特化,它会推导出左值引用。 Live example
  • 作为一种解决方法,如果async 的参数是static_cast&lt;decltype((parallel_sum&lt;RAIter&gt;))&gt;(parallel_sum),clang 会编译:O

标签: c++ gcc c++11 clang libstdc++


【解决方案1】:

我认为这是 clang++ 中的一个错误。除非有我不知道的奇怪的限制规则,否则引用函数的 id 表达式是左值。但是,clang++在推导中区分了函数模板特化和普通函数,以供通用引用:

#include <iostream>

template<class T>
void print_type()
{
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

template <class T>
int foo(bool) { return 42; }

int bar(bool) { return 42; }

template<class T>
void deduce(T&&)
{
    print_type<T>();
}

int main()
{
    deduce(foo<bool>);
    deduce(bar);
}

输出,clang++ 直到并包括早期的 3.5:

void print_type() [T = int (bool)] void print_type() [T = int (&)(bool)]

Live example


std::result_of用于libstdc++对std::async的实现,获取函数的返回类型(snippet from here):

template<typename _Fn, typename... _Args>
future<typename result_of<_Fn(_Args...)>::type>
async(launch __policy, _Fn&& __fn, _Args&&... __args)

如果我们将foo&lt;bool&gt; 作为第二个参数传递,clang++ 会推导出_Fn == int (bool)

函数(对象)的类型与result_of 的参数类型相结合。这可能是 C++03 的遗留物,我们还没有可变参数模板。传递参数类型以允许 result_of 解析重载函数,例如重载的 operator(),以防 _Fn 是类类型。

但是,如果_Fn 不是函数引用,而是函数类型,则_Fn(_Args...) 的组合构成非法类型:函数返回函数:

_Fn == int(bool) _Args... == 布尔值 ==> _Fn(_Args...) == int(bool)(bool)

但还有更多:async 的上述声明有缺陷,请参阅LWG 2021Howard Hinnant changed the declaration in libc++转:

template <class F, class... Args>
future < typename result_of<
             typename decay<F>::type(typename decay<Args>::type...)
         >::type
       >
async(launch policy, F&& f, Args&&... args);

所以 libc++ 将函数衰减为函数指针。缺少左值引用导致的问题消失了。

【讨论】:

    猜你喜欢
    • 2020-09-10
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 2016-08-22
    • 2012-04-21
    • 1970-01-01
    • 2012-05-24
    相关资源
    最近更新 更多