Search code examples
c++templatesc++17template-argument-deductiondeduction-guide

Is it possible to get a template function to deduce the type of a template argument using either conversion or deduction guide?


Is it possible to get the compiler to deduce the type for the template function call, using either the type conversion or the deduction guide? And, if not, why not?

#include <iostream>

template<typename T>
class Wrapper
{
public:
    Wrapper(T&& elmt)
        : m_data(std::forward<T>(elmt))
    {   }

    const T& data() const
    {
        return m_data;
    }

private:
    T m_data;
};

template<typename T>
Wrapper(T&& elmt) -> Wrapper<T>;

template<typename T>
void someFunc(const Wrapper<T>& wrapper)
{
    std::cout << wrapper.data() << std::endl;
}

int main()
{
    // Any chance this could work?
    // someFunc("Make me a Wrapper<const char*> please!");     //fails
    // someFunc({"Make me a Wrapper<const char*> please!"});   //fails

    // This works, but that's what I'd like to avoid
    someFunc(Wrapper{"This one works"});

    return 0;
}

(Compiler Explorer link: https://godbolt.org/z/eGs3raMY4)

If Wrapper wasn't a template, this would work directly:

#include <iostream>

class StrWrapper
{
public:
    StrWrapper(const char* str)
        : m_data(str)
    {   }

    const char* data() const { return m_data; }

private:
    const char* m_data;
};

void strFunc(const StrWrapper& wrapper)
{
    std::cout << wrapper.data() << std::endl;
}

int main()
{
    strFunc("This works, right?");

    return 0;
}

(Compiler Explorer: https://godbolt.org/z/nnoaPcs91)

I know I could add an overload for each type I want the deduction for, but in this case it isn't a very practical solution (many overloads required).


Solution

  • Is it possible to get the compiler to deduce the type for the template function call, using either the type conversion or the deduction guide?

    You can do a recursive approach here

    // variable template for checking the "Wrapper<T>" type
    template<typename T> inline constexpr bool is_Wrapper = false;
    template<typename T> inline constexpr bool is_Wrapper<Wrapper<T>> = true;
    
    template<typename T>
    void someFunc(T&& arg)
    {
        if constexpr (is_Wrapper<T>)  // if T == Wrapper<T>
            std::cout << arg.data() << std::endl;
        else  // if T != Wrapper<T>, construct explicitly and pass to the someFunc()
            someFunc(Wrapper<T>{ std::forward<T>(arg) });
    }
    

    This allows the both

    someFunc("Make me a Wrapper<const char*> please!");  // works
    someFunc(Wrapper{ "This one works" });               // works
    

    See a live demo in godbolt.org