Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is GCC producing a strange error and trying to call the wrong method when template arguments are specified explicitly?

I had a function submitAsync which accepted a templated std::function as a parameter:

template <typename Ret, typename... Args>                                                                                                                                                     
Future<Ret> submitAsync(const function<Ret (Args...)> &func, Args&&... args);

However, implicit template argument deduction wasn't working when passing a lambda (similar to the issue here, so I had to make a more generic function that accepted the function as a template parameter, to then pass it to the original function:

template <typename Func, typename... Args>
auto submitAsync(Func &&func, Args&&... args) -> // Line 82, where the strange error occurs
    Future<
        typename enable_if<
            is_convertible<
                Func, function<decltype(func(args...)) (Args...) >
            >::value ,  decltype(func(args...))
        >::type
    > {
    typedef decltype(func(args...)) ReturnType;
    return submitAsync<ReturnType, Args...>(function<ReturnType (Args...)>(func), forward<Args>(args)...);
}

This compiles fine with Clang, but with GCC returns the following error:

src/Scheduler.hpp: In substitution of ‘template<class Func, class ... Args> Future<typename std::enable_if<std::is_convertible<Func, std::function<decltype (func(MCServer::Scheduler::startThread::args ...))(Args ...)> >::value, decltype (func(args ...))>::type> MCServer::Scheduler::submitAsync(Func&&, Args&& ...) [with Func = int; Args = {}]’:
src/Scheduler.hpp:91:109:   required from ‘Future<typename std::enable_if<std::is_convertible<Func, std::function<decltype (func(MCServer::Scheduler::startThread::args ...))(Args ...)> >::value, decltype (func(args ...))>::type> MCServer::Scheduler::submitAsync(Func&&, Args&& ...) [with Func = MCServer::MinecraftServer::init()::<lambda()>&; Args = {}; typename std::enable_if<std::is_convertible<Func, std::function<decltype (func(MCServer::Scheduler::startThread::args ...))(Args ...)> >::value, decltype (func(args ...))>::type = int]’
src/MinecraftServer.cpp:237:37:   required from here
src/Scheduler.hpp:82:10: error: expansion pattern ‘#‘nontype_argument_pack’ not supported by dump_expr#<expression error>’ contains no argument packs

This shows firstly that the lines

return submitAsync<ReturnType, Args...>(function<ReturnType (Args...)>(func), forward<Args>(args)...); 

which should be calling submitAsync(const function<Ret (Args...)> &, Args&&...), is actually trying to call submitAsync(Func &&func, Args&&... args), which of course doesn't work as the type of func being passed is int. The last part of the error I also do not understand, expansion pattern ‘#‘nontype_argument_pack’ not supported by dump_expr#<expression error>’ contains no argument packs which could perhaps be a compiler bug (line 82 is the main part of the function's signature, where I put a comment to mark it)?

Strangely enough, when I remove the explicit template parameters in the call to submitAsync, replacing this line:

return submitAsync<ReturnType, Args...>(function<ReturnType (Args...)>(func), forward<Args>(args)...);

with this:

return submitAsync(function<ReturnType (Args...)>(func), forward<Args>(args)...);

GCC compiles it correctly. So, why is GCC calling the wrong function when the template arguments are specified, even though it works fine when allowed to deduce the arguments? And can anyone tell me what the strange error on line 82 is?

Edit: Forgot to mention, I'm using GCC 4.7.2

EDIT 2: solution and explanation here

like image 359
Daniel Mulcahy Avatar asked Oct 15 '12 17:10

Daniel Mulcahy


1 Answers

Upon further testing, I've realised that both Clang and GCC without explicit template parameters wasn't actually working as I wished. In both cases, the function was just calling itself as the first function that fit the parameters. It was caused by the compiler deciding the submitAsync that takes the function as a template parameter was a better match than the one taking a const std::function&. It works fine now after changing that submitAsync to take the function by reference:

template <typename Ret, typename... Args>
Future<Ret> Scheduler::submitAsync(function<Ret (Args...)> func, Args&&... args)
like image 56
Daniel Mulcahy Avatar answered Nov 14 '22 22:11

Daniel Mulcahy