【问题标题】:Use Implicit conversion before Templating在模板之前使用隐式转换
【发布时间】:2021-12-15 08:15:47
【问题描述】:

我正在尝试结合使用模板专业化和隐式转换来将模板类型分为 3 个类别:

  1. 可以隐式转换为字符串的类型
  2. 可以隐式转换为双精度的类型
  3. 不能隐式转换为字符串或双精度的类型

然而,在我的这次尝试中,所有可以转换为双精度或字符串的类型都以 T 类型的默认情况结束。

template<>
void Foo(std::string s)
{
    //can be converted to a string
}

template<>
void Foo(double d)
{
    //can be converted to a double
}

template<typename T>
void Foo(T t)
{
    //cannot be converted to a string or a double 
}

struct Other {};

int main()
{
    Foo("bar");// creates Foo(const char*) instead of converting to string
    Foo(1);// creates Foo(int) instead of converting to double
    Other o
    Foo(o);// creates Foo(Other)
}

另一方面,如果模板被移除,隐式转换仍然有效,但代价是无法处理“两种”类型。

void Foo(std::string s)
{
    //can be converted to a string
}

void Foo(double d)
{
    //can be converted to a double
}

struct Other {};

int main()
{
    Foo("bar");// const char* -> std::string
    Foo(1);// int -> double
    Other o
    Foo(o);// No overload available for this type
}

有没有办法在创建新的模板化函数之前优先考虑隐式转换到现有的专用模板?或者也许我应该以完全不同的方式来解决这个问题?

【问题讨论】:

标签: c++ template-specialization overload-resolution


【解决方案1】:

不要特化...老实说,如果解决方案 Y 存在问题 X,那么特化函数模板几乎总是 Z。要调用特化,推导出的模板参数必须完全正确 em> 我们专门研究的那些。此外,是否存在特化甚至不考虑重载决议!重载由主模板声明单独解决。

对于您的问题,您需要重载,以便在重载解决方案中始终考虑这两种自定义情况。然后,您只需确保在需要时简单地丢弃通用的 catch-call 函数。 SFINAE 以及一些标准类型特征将完全实现这一点。

#include <type_traits>

// Overloads. Not templates!

void Foo(std::string s)
{
    //can be converted to a string
}


void Foo(double d)
{
    //can be converted to a double
}

template<typename T>
std::enable_if_t<!(std::is_convertible_v<T, std::string> || std::is_convertible_v<T, double>)>
Foo(T t)
{
    //cannot be converted to a string or a double 
}

查看条件是否正是您想要检查的?类型特征验证是否可以进行转换,在这种情况下,std::enable_if_t 是一个格式错误的类型(不存在)。所以重载被简单地丢弃(因为S替换Failure Is Not An E错误)。其他两个重载仍然存在,因此选择其中一个。

如果两种转换都不可能,则不会丢弃模板重载,并且std::enable_if_t - 函数的返回类型 - 为 void(voidenable_if_t 默认解析的内容)。


如果您可以使用支持最新 C++ 标准的较新编译器,您甚至可以以比经典 SFINAE 更用户友好的方式编写它

#include <concepts>

template<typename T>
    requires !(std::convertible_to<T, std::string> || std::convertible_to<T, double>)
void Foo(T t)
{
    //cannot be converted to a string or a double 
}

约束现在可以在声明中占据它自己的位置,而不用返回类型。

【讨论】:

  • 根据用例,我认为 constexpr if + type traits 是比 sfinae 更直接的解决方案,即使使用新的 c++20 方法也是如此。但这是个人品味,仍然是一个很好且有效的答案+1
  • @Wutz - 就像生活中的所有事情一样,这取决于。如果这是许多 TU 使用的标头,并且分支主体处理具体类型,那么我认为 if constexpr 的应用是一种反模式。具体的重载可以与它们的依赖项一起单独编译。这使标题保持整洁和小。 if constexpr 将要求每次都带入整个派对。我更喜欢 if consexpr 分支仍然是通用的。这是一个省去人工标签调度的好工具。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多