【问题标题】:Overloading on (desired) return type重载(期望的)返回类型
【发布时间】:2021-07-15 23:47:30
【问题描述】:

为您自己的类型定义to_string 函数很容易,并且只需通过重载调用它。大部分代码都知道to_string(x) 将通过ADL 找到的约定,模板可以依赖它而不知道x 的类型。

但是相反的情况呢,比如from_string 函数?什么是类型提供自己的实现的方式,以便其他代码可以轻松方便调用它,并且它可以工作模板也一样?

当这些实现本身对某些类型是通用的时,为每种类型使用具有显式特化的单个模板会让人头疼。 (至少,在概念之前的实现中是这样。)

使所需的类型出现在参数列表中,无论是作为虚拟参数还是通过包装输入字符串的模板类型,其优点是函数的选择使用适当的重载分辨率,并且可以简单自然地在不同的实现之间进行选择,包括普通的单一类型函数、处理一组相关类型的模板、隐式转换和继承关系。

虽然它适用于通用代码,但调用起来很尴尬,我不想让它成为调用它的正常方式。

我的项目使用 gcc 8,它支持 C++17 和 Concepts TS。标准库对概念一无所知,但我可以使用 requires 并在自己的代码中创建概念。

什么是定义像这样from_string 这样的函数的好方法?

【问题讨论】:

  • 为什么不是您要转换的类的静态成员。 staitc Blah Blah::from_string(std::string const& stream); 用法:Blah x = Balh::from_string(input);
  • @MartinYork 很多类型都是枚举。

标签: c++ c++17 c++20


【解决方案1】:

要支持模板类,您需要一个可以专门化的结构。

template<class T, class sfinae=void> struct from_string_impl;

然后是一个辅助方法和类型来调用它的operator()

struct from_string_t {
   const std::string& str;
   template<class T>
   operator T() const {return from_string_impl<T>{}(str);}
};
from_string_t from_string(const std::string& str) {return {str};}

还有一些基本的入门实现:

template<> struct from_string_impl<int>{int operator()(const std::string& str) { return std::stoi(str);}};
template<> struct from_string_impl<long>{long operator()(const std::string& str) { return std::stol(str);}};
template<> struct from_string_impl<long long>{long long operator()(const std::string& str) { return std::stoll(str);}};
template<> struct from_string_impl<unsigned long>{unsigned long operator()(const std::string& str) { return std::stoul(str);}};
template<> struct from_string_impl<unsigned long long>{unsigned long long operator()(const std::string& str) { return std::stoull(str);}};
template<> struct from_string_impl<float>{float operator()(const std::string& str) { return std::stof(str);}};
template<> struct from_string_impl<double>{double operator()(const std::string& str) { return std::stod(str);}};
template<> struct from_string_impl<long double>{long double operator()(const std::string& str) { return std::stold(str);}};

在大多数情况下使用是微不足道的:

int i = from_string("304.5");
double d = from_string("304.5");
unsigned long long ull = from_string("304.5");

http://coliru.stacked-crooked.com/a/d4bd08dbe081a156

虽然如果你试图将它传递给一个本身有重载的方法,你会得到编译器错误,因为编译器将无法推断出将返回类型转换为什么类型。

有趣的是,这可以追溯到模板首次添加到 C++ 时的效果。

【讨论】:

  • 你的类模板应该有一个额外的、用于 SFINAE 的默认模板参数。
  • 这就是在允许直接执行之前必须对模板函数进行显式特化的方式。它具有与我在 OP 中解释的完全相同的缺点。事实上,我确实有一个 operator T() 助手,就像你展示的那样!但是调用的底层内容是(当前)使用虚拟参数来支持重载,因为如 OP 中所解释的那样专门化模板的问题。
  • 例如,您的许多“基本入门实现”实际上是整数类型的单个模板。另一个模板是用于基于枚举的强类型,但对于一些需要特殊处理的基于枚举的类型,我仍然需要特定的实现。重载将在模板上选择常规函数;保持 enable_ifs 互斥会很尴尬。
  • @JDługosz:我很难理解这个问题。你能举一个这些基于枚举的强类型的例子,让我明白为什么这很难吗?
  • @MooingDuck 是的,我会在不忙的时候扩展它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-18
  • 2012-03-23
  • 1970-01-01
相关资源
最近更新 更多