- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我的代码如下:
template <typename T, typename sepT = char>
void print2d(const T &data, sepT sep = ',') {
for(auto i = std::begin(data); i < std::end(data); ++i) {
decltype(*i) tmp = *i;
for(auto j = std::begin(tmp); j < std::end(tmp); ++j) {
std::cout << *j << sep;
}
std::cout << std::endl;
}
}
int main(){
std::vector<std::vector<int> > v = {{11}, {2,3}, {33,44,55}};
print2d(v);
int arr[2][2] = {{1,2},{3,4}};
print2d(arr);
return 0;
}
如果我将 decltype
更改为 auto
,它不会编译并报错(部分错误):
2d_iterator.cpp: In instantiation of ‘void print2d(const T&, sepT) [with T = int [2][2]; sepT = char]’:
2d_iterator.cpp:21:21: required from here
2d_iterator.cpp:9:36: error: no matching function for call to ‘begin(const int*&)’
2d_iterator.cpp:9:36: note: candidates are:
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/string:53:0,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/locale_classes.h:42,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/ios_base.h:43,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/ios:43,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/ostream:40,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/iterator:64,
为什么会这样?
最佳答案
答案总结在一条评论中:
decltype
yieldsint(&)[2]
, whilst plainauto
forces a pointer conversion (same rules as template argument deduction). Just useauto&
. - Xeo
@Xeo 的comment-answer 基本上说因为 auto
涉及与 template argument type deduction 相同的规则,所以 auto
推导出一个指针 (int*
) 类型超出源的数组类型(i
,特别是 int(&)[2]
)。
您的代码中有一些很棒的东西:它实际上演示了当参数是引用时模板类型推导的行为以及引用如何影响推导类型的方式。
template <typename T, typename sepT = char>
void print2d(const T &data, sepT sep = ',') {
...
}
...
int arr[2][2] = {{1,2},{3,4}};
print2d(arr);
您可以看到 data
的类型是 const T&
,它是对 const T
的引用。现在,它与 arr
一起传递,其类型为 int[2][2]
,它是由两个 int
组成的两个数组组成的数组>s(哇!)。现在来模板参数类型推导。在这种情况下,它规定 data
是一个 reference,T
应该用参数的原始类型来推导,即 int[2][2]
。然后,它将参数类型的任何限定应用于 parameter,并且 data
的限定类型是 const T&
,应用了 const
和 &
限定符,因此 data
的类型是 const int (&) [2][2]
。
template <typename T, typename sepT = char>
void print2d(const T &data, sepT sep = ',') {
static_assert(std::is_same<T, int[2][2]>::value, "Fail");
static_assert(std::is_same<decltype(data), const int(&)[2][2]>::value, "Fail");
}
...
int arr[2][2] = {{1,2},{3,4}};
print2d(arr);
然而,if data
将是一个 non-reference,模板参数类型推导规则,如果参数的类型是数组类型(例如 int[2][2]
), the array type shall "decay" to its corresponding pointer type ,从而使 int[2][2]
变成 int(*)[2]
(加 const
如果参数是 const
) ( fix courtesy of @Xeo ).
太棒了!我只是解释了完全不是导致错误的部分。 (而且我刚刚解释了很多模板魔法)...
... 没关系。现在到错误。但在我们走之前,请记住这一点:
auto == template argument type deduction
+ std::initializer_list deduction for brace init-lists // <-- This std::initializer_list thingy is not relevant to your problem,
// and is only included to prevent any outbreak of pedantry.
现在,您的代码:
for(auto i = std::begin(data); i < std::end(data); ++i) {
decltype(*i) tmp = *i;
for(auto j = std::begin(tmp); j < std::end(tmp); ++j) {
std::cout << *j << sep;
}
std::cout << std::endl;
}
战斗前的一些先决条件:
decltype(data) == const int (&) [2][2]
decltype(i) == const int (*) [2]
(参见 std::begin
),它是一个指向 int[2]
的指针。现在当你执行 decltype(*i) tmp = *i;
时,decltype(*i)
将返回 const int(&)[2]
,对 int[2]
的引用(记住单词 dereference)。因此,它也是 tmp
的类型。 您使用 decltype(*i)
保留了原始类型。
但是,当你这样做时
auto tmp = *i;
猜猜decltype(tmp)
是什么:int*
!为什么?因为上面所有的 babbery-blablablah,还有一些模板魔法。
那么,为什么 int*
会出错?因为 std::begin
需要一个数组类型,而不是其较小的衰减指针。因此,当 tmp
为 int*
时,auto j = std::begin(tmp)
会导致错误。
如何解决(还有tl;dr)?
保持原样。使用 decltype
。
猜猜看。让你的 auto
ed 变量成为引用!
auto& tmp = *i;
或
const auto& tmp = *i;
如果您不打算修改 tmp
的内容。 (Greatness by Jon Purdy)
故事的寓意:一个伟大的评论可以为一个人节省一千个字。
UPDATE: 将 const
添加到 decltype(i)
和 decltype(*i)
给出的类型中, 因为 std::begin(data)
将返回一个 const
指针,因为 data
也是 const
(fix by litb ,谢谢)
关于c++ - 为什么 decltype 在这里工作,但不是 auto?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21824013/
由 this question 触发,我想知道是否允许这样做: template T foo(){return T{};} struct bar {}; int main() { bar a
我正在尝试以一种通用的方式实现 group_by 方法,我可能已经实现了它(除了它不适用于 C 数组),但代码对我来说仍然很难看...... 有没有更简单的方法来做我想做的事(+让它适用于所有容器和
这个问题在这里已经有了答案: What is decltype with two arguments? (2 个答案) 关闭 7 年前。 我遇到了一个decltype(),有两个参数作为模板函数的返
这是问题的后续:What does the void() in decltype(void()) mean exactly? . decltype(void()) 编译得很好,void() 在这种情况
这是我第一次使用decltype,我不太确定我是否正确使用它。该代码确实编译并且似乎适用于像 char 和 int 这样的 POD。 但是,我想知道我是否会遇到更复杂的数据类型的任何问题 - 其他人警
我一直在使用解析为与声明相同类型的定义中的推导返回类型。这有效: template struct Cls { static std::size_t f(); }; template declt
我发现它们是不同的,并且语言标准规定了每个语句应该检索什么样的类型(变量和表达式之间的差异)。但我真的很想知道为什么这两种类型应该不同? #include int x=0; decltype((x))
关于 decltype(x) 和 decltype((x)) 之间的区别,我已经读过很多遍了。一个例子如下。 x is the name of a variable, so decltype(x) i
我有一个简单的模板化包装器结构,其成员函数在其模板类型的对象上调用 .error()。 template struct Wrapper { T t; decltype(auto) f
decltype(auto) 和 decltype(returning expression) 作为函数(模板)的返回类型 if expr 有什么区别在这两种情况下都使用不带括号? auto f()
我在想 decltype((x)) 给出了 & 引用类型,但是一些实验表明还有其他事情: #include int main(){ int x = 0; decltype((x)) r
例如,简单的恒等仿函数: template class identity { public: constexpr auto operator ()(T && i) -> decltype(s
这个问题在这里已经有了答案: 关闭10 年前。 Possible Duplicate: decltype and parenthesis 我在维基百科上找到了这个: auto c = 0;
// g++ 7.3 template struct td; int main() { int a = 1; td t1; td t2; return 0; } 编译结果如下: 错误:
这确实是一个 C++14 问题。而且它的理论性多于实践性。 有时您会零碎地构建一个函数的结果: int f( int x, int y ) { int a; //... re
我试图检测成员函数 baz() 的存在在模板参数中: template struct ImplementsBaz : public std::false_type { }; template stru
考虑以下代码:(Ideone) struct S { int a() {return 0;} decltype(a()) b() {return 1;} }; 它给了我以下错误: er
(如果您是 C++11 专业人士,请跳至粗体段落。) 假设我想编写一个模板方法,该方法调用并返回传递的对象的结果,该对象的类型是模板参数: template ReturnType doSomethin
代码 #include int main() { int a=3; int *p=&a; decltype (a) k1; decltype (*p) k2;
我是 C++ 新手。我正在尝试学习 decltype 的概念。我在网上看到这段代码。我将 decltype(s1.size()) 更改为 int,代码工作正常。 decltype(s1.size())
我是一名优秀的程序员,十分优秀!