是否可以使用转换或演绎指南获得模板函数来推断模板参数的类型?

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

提问人:Michel 提问时间:3/11/2023 更新时间:3/11/2023 访问量:70

问:

是否可以让编译器使用类型转换或推导来推断模板函数调用的类型? 如果没有,为什么不呢?

#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;
}

(编译器资源管理器链接:https://godbolt.org/z/eGs3raMY4)

如果 Wrapper 不是模板,这将直接起作用:

#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;
}

(编译器资源管理器:https://godbolt.org/z/nnoaPcs91)

我知道我可以为我想要扣除的每种类型添加一个重载,但在这种情况下,这不是一个非常实用的解决方案(需要许多重载)。

C++ C++17 模板 参数-演绎 演绎指南

评论

0赞 Michel 3/11/2023
我发现我可以使用某种 Functor 类来处理我想要的行为:godbolt.org/z/WsdnPGaPP

答:

0赞 NathanOliver 3/11/2023 #1

与其使用包装器,不如使用包装器,然后在函数中构造一个包装器,例如someFuncT

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

这允许

someFunc("Make me a Wrapper<const char*> please!"); 

进行编译,如此实时示例所示。


两者兼而有之的原因

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

失败是它们不是 的,编译器不会尝试转换参数,因此编译器无法推断出应该是什么。WrapperT

评论

0赞 Michel 3/11/2023
我认为这就是我不明白的地方:为什么编译器不尝试转换?我想,如果它不是模板函数,那会。是不是模板推导先发生并失败,所以没有候选函数可以尝试转换?
0赞 NathanOliver 3/11/2023
@Michel是的。模板推导不进行转换,因为参数基本上可以转换为无限数量的类型。
0赞 Michel 3/11/2023
谢谢,这是有道理的:)这让我不禁要问,我们是否应该为函数模板提供演绎指南:如果提供了演绎指南,它适用于构造函数,而构造函数是一种函数~如果能对此进行概括就好了。
0赞 NathanOliver 3/11/2023
@Michel 我们几乎已经有了,这叫做超载。您可以利用重载,例如void someFunc(const char* str) { someFunc(Wrapper{str}); }
3赞 JeJo 3/11/2023 #2

是否可以让编译器使用类型转换或推导来推断模板函数调用的类型?

你可以在这里做一个递归方法

// 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) });
}

这允许两者

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

观看 godbolt.org 中的现场演示