Search code examples
c++c++11templatesvariadic-templatestemplate-argument-deduction

Deduced conflicting types in template pack with reference


I'm working on a program with following structure:

#include <iostream>
#include <string>

void fun(const std::string &text, int a, int b) { // (1)
    std::cout << text << a + b << std::endl;
}

template<typename ...Args>
void execute(void(*fun)(Args...), Args ...args) {
    fun(args...);
}

void init(const std::string &text, int a, int b) {
    execute(fun, text, a, b);
}

int main() {
    init("Fun: ", 1, 2);
    return 0;
}

and I get the error message

.code.tio.cpp:14:2: error: no matching function for call to 'execute'
        execute(fun, text, a, b);
        ^~~~~~~
.code.tio.cpp:9:6: note: candidate template ignored: deduced conflicting types for parameter 'Args' (<const std::__cxx11::basic_string<char> &, int, int> vs. <std::__cxx11::basic_string<char>, int, int>)
void execute(void(*fun)(Args...), Args ...args) {
     ^
1 error generated.

I can fix the error by removing the reference in line (1):

void fun(const std::string text, int a, int b) {

but I want to pass the values by reference and not by value. The function template

template<typename ...Args>
void execute(void(*fun)(Args...), Args ...args)

must not be changed. How can I fix this so that text is passed by reference, execute is not changed and init is also not changed if possible?

EDIT: @super showed that I was wrong and I have to reformulate my requirements. execute can only be modified to the extent that other projects that have a dependency on this function don't break. I didn't think about such a solution.


Solution

  • Without touching execute, I think you have to change init(). One way would be to explicitely pass the template argument (bypassing the argument deduction in order to transport the reference type information):

    void init(const std::string &text, int a, int b) {
        execute<const std::string&>(fun, text, a, b);
    }