【问题标题】:Template deduction with leading pack expansion fails on GCC 4.8.2 x86 (devtoolset-2) only仅在 GCC 4.8.2 x86 (devtoolset-2) 上使用前导包扩展的模板推导失败
【发布时间】:2016-12-26 13:41:17
【问题描述】:

这是我的 C++11 项目中一些模板元黑客的 MCVE:

#include <functional>

struct Foo {};

template <typename... T>
void expect(std::function<void(const T&)>&&... onSuccess)
{
    expect<T...>(
        std::forward<std::function<void(const T&)>>(onSuccess)...,
        0,
        [](){}
    );
}

template <typename... T>
void expect(
  std::function<void(const T&)>&&... onSuccess,
  const time_t timeout,
  std::function<void()>&& onExpiry
)
{
    // ...
}

int main()
{
    expect<Foo>([=](const Foo&) {}, 42u, [=](){});
}

它在 GCC 6.1(using Coliru)、GCC 4.8.5 和 GCC 4.8.2 x86_64(使用 GodBolt 中构建良好,但是当我插入它进入我的开发环境(这是通过 CentOS 6 x86 上的 devtoolset-2 的 GCC 4.8.2)我收到错误:

[root@localhost ~]# g++ test.cpp -std=c++11 -o test
test.cpp: In function ‘int main()’:
test.cpp:27:47: error: no matching function for call to ‘expect(main()::__lambda1, unsigned int, main()::__lambda2)’
   expect<Foo>([=](const Foo&) {}, 42u, [=](){});
                                               ^
test.cpp:27:47: note: candidates are:
test.cpp:6:6: note: template<class ... T> void expect(std::function<void(const T&)>&& ...)
 void expect(std::function<void(const T&)>&&... onSuccess)
      ^
test.cpp:6:6: note:   template argument deduction/substitution failed:
test.cpp:27:47: note:   mismatched types ‘std::function<void(const T&)>’ and ‘unsigned int’
   expect<Foo>([=](const Foo&) {}, 42u, [=](){});
                                               ^
test.cpp:16:6: note: template<class ... T> void expect(std::function<void(const T&)>&& ..., time_t, std::function<void()>&&)
 void expect(
      ^
test.cpp:16:6: note:   template argument deduction/substitution failed:
test.cpp:27:47: note:   cannot convert ‘<lambda closure object>main()::__lambda1{}’ (type ‘main()::__lambda1’) to type ‘time_t {aka long int}’
   expect<Foo>([=](const Foo&) {}, 42u, [=](){});

[root@localhost ~]# g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-2/root/usr/libexec/gcc/i686-redhat-linux/4.8.2/lto-wrapper
Target: i686-redhat-linux
Configured with: ../configure --prefix=/opt/rh/devtoolset-2/root/usr --mandir=/opt/rh/devtoolset-2/root/usr/share/man --infodir=/opt/rh/devtoolset-2/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --enable-languages=c,c++,fortran,lto --enable-plugin --with-linker-hash-style=gnu --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.2-20140120/obj-i686-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.2-20140120/obj-i686-redhat-linux/cloog-install --with-mpc=/builddir/build/BUILD/gcc-4.8.2-20140120/obj-i686-redhat-linux/mpc-install --with-tune=generic --with-arch=i686 --build=i686-redhat-linux
Thread model: posix
gcc version 4.8.2 20140120 (Red Hat 4.8.2-15) (GCC)

我可以通过在第二次重载中将 onSuccess 移动到 timeoutonExpiry 之后来解决此问题,但我认为原始排序更易于开发人员使用,如果可以的话,我宁愿保留它。

那么还有其他方法吗?

而且,对于奖励积分,实际发生了什么?

【问题讨论】:

  • 如果有的话,您可以在 Godbolt 上使用 gcc 4.8.1 进行复制。
  • @Holt:嘿,好地方。
  • 此外,如果您反转 expect 函数的声明,它将与 gcc 4.8.1 (godbolt) 一起编译。
  • @Holt:我爱你!要写答案吗?
  • 我无法真正解释,但可以肯定... ;)

标签: c++ c++11 gcc variadic-templates devtoolset


【解决方案1】:

我真的不知道“为什么”,但如果你反转两个声明,它就会起作用:

#include <functional>

struct Foo {};

template <typename... T>
void expect(
  std::function<void(const T&)>&&... onSuccess,
  const time_t timeout,
  std::function<void()>&& onExpiry
)
{ 
}

template <typename... T>
void expect(std::function<void(const T&)>&&... onSuccess)
{

    expect<T...>(
        std::forward<std::function<void(const T&)>>(onSuccess)...,
        0,
        [](){}
    );
}

int main()
{
    expect<Foo>([=](const Foo&) {}, 42u, [=](){});
    expect<Foo, Foo>([=](const Foo&) {}, [=](const Foo&) {}, 42u, [=](){});
    expect<Foo, Foo>([=](const Foo&) {}, [=](const Foo&) {});
}

【讨论】:

  • 这神奇地修复了上面的测试用例。可悲的是,它并没有修复我的真实代码,它——已经做出了这个改变——显然现在在其他地方也有类似的问题。但这不是你的错。我将继续沿着这条推理线......
  • 大声笑,好笑,如果事先在其他地方声明了另一个这样的函数,似乎会中断......即使在完全不同的范围和不同的名称中。比如说,struct Bar 的成员。猜猜可变参数在 4.8.1/2 中有点坏了
  • @LightnessRacesinOrbit Magic 不幸地在编程中从来没有很好的工作...... ;) 你能提供一个失败的测试用例吗?
  • here 虽然您必须复制/粘贴到 GodBolt obvs 中。顺便说一句,我根据您的建议删除了转发:)
  • 啊,对不起,错字...改用这个coliru.stacked-crooked.com/a/4585b45ac01f32f7
【解决方案2】:

仔细查看 GodBolt 界面会发现“4.8.2 x86_64”实际上是 4.8.5(并且“4.8.1 x86_64”重现),所以这似乎是一个简单的 GCC 错误(尽管我可以在 Bugzilla 上看不到)。

然后,我将使用重新排序的解决方法,将可变参数保持在右侧。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多