concat中的可变模板和推断的返回类型
Variadic template and inferred return type in concat
这很有效
利用C++11,我试图构建一个函数,通过将任意对象写入ostringstream
来连接它们。作为这些的助手函数,我有一个可变助手函数,它将单个项目附加到现有的ostream
(下面的完整粘贴中给出了更多上下文):
template<class Head, class... Tail>
std::ostream& append(std::ostream& out, const Head& head, const Tail&... tail)
{
return append(out << head, tail...);
}
此操作失败
但后来我想,当<<
-应用于流时,可能会有一些对象不会返回ostream,而是返回一些占位符。因此,让流类型也作为模板参数会很酷:
1 #include <iostream>
2 #include <sstream>
3
4 template<typename Stream>
5 Stream& append(Stream& out) {
6 return out;
7 }
8
9 template<class Stream, class Head, class... Tail>
10 auto append(Stream& out, const Head& head, const Tail&... tail)
11 -> decltype(append(out << head, tail...)) // <<<<< This is the important line!
12 {
13 return append(out << head, tail...);
14 }
15
16 template<class... Args>
17 std::string concat(const Args&... args) {
18 std::ostringstream s;
19 append(s, args...);
20 return s.str();
21 }
22
23 int main() {
24 std::cout << concat("foo ", 3, " bar ", 7) << std::endl;
25 }
但是g++-4.7.1
将拒绝编译这个。
将签名中Stream
的所有用途改回std::ostream
不会让它变得更好,所以我认为新的函数声明语法在这里起着重要作用——尽管gcc声称自4.4以来就支持它。
错误消息
错误消息相当神秘,并没有告诉我这里发生了什么。但也许你能理解它。
In instantiation of ‘std::string concat(const Args& ...) [with Args = {char [5], int, char [6], int}; std::string = std::basic_string<char>]’:
24:44: required from here
19:3: error: no matching function for call to ‘append(std::ostringstream&, const char [5], const int&, const char [6], const int&)’
19:3: note: candidates are:
5:9: note: template<class Stream> Stream& append(Stream&)
5:9: note: template argument deduction/substitution failed:
19:3: note: candidate expects 1 argument, 5 provided
10:6: note: template<class Stream, class Head, class ... Tail> decltype (append((out << head), append::tail ...)) append(Stream&, const Head&, const Tail& ...)
10:6: note: template argument deduction/substitution failed:
In substitution of ‘template<class Stream, class Head, class ... Tail> decltype (append((out << head), tail ...)) append(Stream&, const Head&, const Tail& ...) [with Stream = std::basic_ostringstream<char>; Head = char [5]; Tail = {int, char [6], int}]’:
19:3: required from ‘std::string concat(const Args& ...) [with Args = {char [5], int, char [6], int}; std::string = std::basic_string<char>]’
24:44: required from here
10:6: error: no matching function for call to ‘append(std::basic_ostream<char>&, const int&, const char [6], const int&)’
10:6: note: candidate is:
5:9: note: template<class Stream> Stream& append(Stream&)
5:9: note: template argument deduction/substitution failed:
10:6: note: candidate expects 1 argument, 4 provided
问题
所以我的核心问题是:
此代码失败有充分的原因吗
我感兴趣的要么是标准中的一些引用,它说我的代码是无效的,要么是对实现中出现的问题的一些见解。如果有人为此找到了一个gcc错误,那也是一个答案。我一直找不到合适的报告。尽管使用std::ostream
只对我当前的应用程序有效,但实现这一点的方法也很好。关于其他编译器如何处理这一问题的输入也很受欢迎,但这还不足以让我接受。
3.3.2【basic.scope.pdecl】
-1-名称的声明点紧接在其完整声明符之后(第8条)和初始值设定项之前(如果有的话),除非以下所述。
函数声明符包含尾部返回类型,因此函数自身的名称不在其尾部返回类型的作用域中。
因此,在表达式decltype(append(out << head, tail...))
中,唯一的候选函数是非变元append(Stream&)
,当参数包tail
不为空时,它不能使用,因此当使用两个以上的参数调用append
时,推导总是失败。
因此GCC拒绝代码是正确的。
去年12月,标准委员会成员对此进行了讨论,并将其作为核心问题进行了报告,见CWG 1433。
我现在能想到的唯一解决方法是尝试使用common_type
,它对某些情况有效,但对其他情况可能失败:
template<class Stream, class Head, class... Tail>
auto append(Stream& out, const Head& head, const Tail&... tail)
-> typename std::common_type<decltype(out << head), decltype(out << tail)...>::type
如果out << head << tail
有效,但out << tail
无效,或者如果任何operator<<
调用返回的内容无法转换为其他operator<<
调用返回的类型,则此操作将失败。
- 如何获取std::result_of函数的返回类型
- 奇怪的结构&GCC&clang(void*返回类型)
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 函数作为模板参数,是否对返回类型强制约束
- C++中函数的向量返回类型引发错误
- 检查函数返回类型是否与STL容器类型值相同
- 为什么返回类型中需要typename?C++
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 警告:在函数返回类型 [-Wignore 限定符] 时忽略类型限定符
- 为什么 c++(g++) 不允许模板返回类型和函数名称之间有空格?
- 为什么返回类型的'const'限定符对标有 __forceinline/内联的函数没有影响?
- 推导 std::vector::back() 的返回类型
- 在 c++ 中将函数返回类型指定为模板参数
- 使用 SWIG 更改生成的 CS 函数中的返回类型
- QtQuick - qml:28:错误:未知方法返回类型:自定义类型
- 基于返回类型的转换和过载扣除
- concat中的可变模板和推断的返回类型