我真的希望能够拥有一个适应任何类型的自由函数。
e.g。
template <typename T> bool ReadLine(T & reader, std::string & line)
{
return reader.ReadString(line);
}
对于某些T,正确的函数是reader.ReadString(buffer)。但对于其他人来说,它应该是reader.ReadLine(缓冲区)。当然,未来可能还有其他模式。关键是要使自由函数ReadLine(从,进入)适应任何合理的一组来自&amp; into(我强制目标缓冲区是一个std :: string来简化这里的事情)。
现在,我可以为我想要的任何具体类型创建一个非模板版本的ReadLine,但我真正需要的是能够对类型类进行部分专门化,例如那些支持模式阅读器的类.ReadString()最终都使用它,而那些支持reader.ReadLine()的人使用它,并且将来我可以添加其他模式而不会打扰任何已经有效的模式。
我知道我可以创建一个策略类,例如LineReaderPolicy,它知道给定的T使用哪种模式(根据T将其部分专用,以将其映射到正确的模式。
但是有更好的,更多的C ++ 14解决方法吗?
这是其中之一“上帝看起来模板真的非常接近真实,非常有用......但对于这种不断反复出现的问题...”
C ++ 11/14的兼容性比以往任何时候都好,但似乎这个基本问题仍未得到解决?或者是它?!
您如何建议我编写一组适应任何合理T的自由函数来读取它的一行? T是字符串流,输出迭代器,文件句柄,字符串,字符串视图等等......
我不能认为C ++真的成熟了,直到我能写出一个合理的
template <typename T> size_t length(T t) { return t.size(); }
然后我可以将其扩展到任何合理的T,并且不必编写知道T的许多细节的代码,但是可以通过这种灵活的自由函数适配器与吨Ts进行互操作......
答案 0 :(得分:8)
如果您可以确保最多定义reader.ReadString
或reader.ReadLine
中的一个,请使用SFINAE来控制重载(Live at Coliru):
template <typename T>
auto ReadLine(T& reader, std::string& line) -> decltype(reader.ReadString(line)) {
return reader.ReadString(line);
}
template <typename T>
auto ReadLine(T& reader, std::string& line) -> decltype(reader.ReadLine(line)) {
return reader.ReadLine(line);
}
不适用的实现将触发SFINAE并从过载集中删除,只留下正确的实现。
在C ++ 14中,您可以省略尾随返回类型,只需使用返回类型演绎。
在将来的C ++版本中,Concepts Lite将使这一过程更加干净。鉴于歧视两种不同类型读者的概念 - 比如StringReader
和LineReader
:
template <typename T>
concept bool StringReader() {
return requires(T& reader, std::string& line) {
{reader.ReadString(line)} -> bool;
};
}
template <typename T>
concept bool LineReader() {
return requires(T& reader, std::string& line) {
{reader.ReadLine(line)} -> bool;
};
}
您将能够直接将您的实现约束到为这些概念建模的类型集:
bool ReadLine(StringReader& reader, std::string& line) {
return reader.ReadString(line);
}
bool ReadLine(LineReader& reader, std::string& line) {
return reader.ReadLine(line);
}
希望您能够在其他地方重复使用这些概念来证明&#34; new-special-better&#34;语法比旧讨厌的语法长得多。 Concepts Lite还可以通过明确的消歧来处理模拟两个概念的类型:
template <typename T>
requires StringReader<T>() && LineReader<T>()
bool ReadLine(T& reader, std::string& line) {
// Call one, or the other, or do something completely different.
}
答案 1 :(得分:3)
我不能认为C ++真的成熟了,直到我能写出一个合理的
template <typename T> size_t length(T t) { return t.size(); }
然后我可以将其扩展到任何合理的T,并且不必编写知道T的许多细节的代码,但是可以通过这种灵活的自由函数适配器与吨Ts进行互操作......
你想要Concepts Lite,我希望它能在C ++ 17中出现:
template<typename T>
concept bool Has_size()
{
return requires(T t) {
t.size() -> Integral;
};
}
template<typename T>
concept bool Has_length()
{
return requires(T t) {
t.length() -> Integral;
};
}
template <Has_size T> auto length(T t) { return t.size(); }
template <Has_length T> auto length(T t) { return t.length(); }
在此之前,您可以使用SFINAE来模拟它,这可以通过多种方式完成,您的示例中最简洁的可能只是一个尾随返回类型,如Casey的回答所示。
template <typename T>
auto length(T t) -> decltype(t.size()) { return t.size(); }
template <typename T>
auto length(T t) -> decltype(t.length()) { return t.length(); }
答案 2 :(得分:1)
template<typename>struct type_sink{typedef void type;};
template<typename T>using TypeSink=typename type_sink<T>::type;
template<typename T,typename=void>
struct has_x:std::false_type{};
template<typename T>
struct has_x<T,TypeSink(decltype(std::declval<T>().x())>:std::true_type{};
是一个非常简单的traits类,用于&#39;类型是否有.x()
方法,并且可以推广。
然后我们可以使用标签调度将我们的功能引导到自定义实现。
template<typename T>
bool do_x_helper(T&t, std::true_type ){
return t.x();
}
template<typename T>
bool do_x_helper(T7, std::false_type ){
// alternative
}
template<typename T>
bool do_x(T& t){
return do_x_helper( t, has_x<T>() );
}
此技术可让您拥有复杂的测试和方法体。您基本上必须手动执行重载解析和调度,但它可以让您完全控制。它类似于std
算法用于调度迭代器类型的技术:在这种情况下,调度不仅仅是true_type
和false_type
。