Вот MCVE из некоторого мета-хакера шаблона в моем проекте C ++ 11:
#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(используя Coliru), GCC 4.8.5 и GCC 4.8.2 x86_64(с помощью GodBolt), но когда я подключаю его к своей среде разработки (которая является GCC 4.8.2 через devtoolset-2 на CentOS 6 x86), я получаю ошибки:
[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
после timeout
а также onExpiry
во второй перегрузке, но я думаю, что оригинальный порядок проще для разработчиков, и я предпочел бы сохранить его, если смогу.
Так есть ли другой способ?
И что касается бонусных баллов, что на самом деле происходит?
Я действительно не знаю «почему», но это работает, если вы инвертируете обе декларации:
#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&) {});
}
Более внимательный взгляд на интерфейс GodBolt показывает, что «4.8.2 x86_64» на самом деле 4.8.5 (и воспроизводит «4.8.1 x86_64»), так что в конце концов это может показаться простой ошибкой GCC (хотя я не вижу это на Bugzilla).
Тогда я просто перейду к обходному пути переупорядочения, оставив аргумент вариации справа.