4
votes

I'm working on a function which invokes a supplied function with a variable number of arguments. It compiles and works correctly on Visual Studio 2015, but fails to compile on Clang . I've prepared a demonstration which shows what I'm trying to do. The error I get in Clang is:

prog.cpp: In function 'int main()': prog.cpp:31:2: error: no matching function for call to 'run(std::vector&, void ()(int&, const int&), const int&)' ); ^ prog.cpp:7:6: note: candidate: template void run(std::vector&, const std::function&, mutrArgs ...) void run( ^ prog.cpp:7:6: note: template argument deduction/substitution failed: prog.cpp:31:2: note: mismatched types 'const std::function' and 'void ()(int&, const int&)' );

#include <functional>
#include <iostream>
#include <vector>
using namespace std;

template<int RepeatTimes, class ... mutrArgs>
void run(
    vector<int>& vec,
    const function<void(int&, mutrArgs ...)>& mutr,
    mutrArgs ... args
)
{
    for (int times{0} ; times < RepeatTimes ; ++times)
        for (auto& item : vec)
            mutr(item, args...);
}

void adder(int& i, const int& val)
{
    i += val;
}

int main()
{
    vector<int> v{0,1,2,3,4,5,6,7,8,9};
    const int addValue{4};
    run<2, const int&>(
        v,
        &adder,
        addValue
    );
    for (auto i : v)
        cout << i << " ";
    cout << endl;
    return 0;
}
2
Instead of using std::function, make the type a template too, like all standard algorithm functions.Some programmer dude
In the non demonstration function, it's required to take a set number of arguments.JadziaMD
If you use std::function<void(int&,const int&)>(&adder) it compiles. Not sure why it needs to be specified though. Might have to do with the pack part not being deducible but I am uncertain of the rules.NathanOliver
Or if you pass by function pointer for run<2, const int&>, it works too DemoJarod42
using std::function<decltype(adder)>(adder) compile with clang++ 3.5 and with g++ 4.9.2max66

2 Answers

1
votes

run<2, const int&> just state the first argument, but doesn't deactivate deduction.

run<2, const int&>(v, &adder, addValue);

has 2 places to deduce mutrArgs:

  • addValue -> mutrArgs = { const int& }

  • &adder which is not a std::function and so fail.

Taking address of function fix that problem

auto call_run = &run<2, const int&>;
call_run(v, &adder, addValue);

Strangely, clang doesn't support the inlined usage contrary to gcc :/

(&run<2, const int&>)(v, &adder, addValue);

If you want to disable deduction, you may make your template arg non deducible:

template <typename T> struct identity { using type = T; };

template <typename T> using non_deducible_t = typename identity<T>::type;

And then

template<int RepeatTimes, class ... mutrArgs>
void run(
    std::vector<int>& vec,
    const std::function<void(int&, non_deducible_t<mutrArgs> ...)>& mutr,
    non_deducible_t<mutrArgs> ... args
)

Demo

Even if in your case a simple typename F as suggested by Joachim Pileborg seems better.

1
votes

If you look at all standard library algorithm function, at least the ones taking a "predicate" (a callable object) they take that argument as a templated type.

If you do the same it will build:

template<int RepeatTimes, typename F, class ... mutrArgs>
void run(
    vector<int>& vec,
    F mutr,
    mutrArgs ... args
)
{
    ...
}

See here for an example of you code. Note that you don't need to provide all template arguments, the compiler is able to deduce them.