为什么 decltype 在这里有效但不能自动?
Why decltype works here but not auto?
我有如下代码:
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
产生int(&)[2]
,而纯auto
强制指针转换(与模板参数推导相同的规则)。只需使用auto&
.- 异
@Xeo的注释答案基本上是说,因为auto
涉及与模板参数类型推导相同的规则,auto
从源的数组类型(i
,特别是int(&)[2]
)中推导出一个指针(int*
)类型。
您的代码中有一些很棒的东西:它实际上演示了当参数是引用时模板类型推断的行为方式,以及引用如何影响类型的推导方式。
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
数组组成的数组(哇!现在来模板参数类型推导。在这种情况下,它规定以data
为参考,T
应用参数的原始类型(即int[2][2]
)推导。然后,它将参数类型的任何限定应用于参数,并且const T&
data
的限定类型,应用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);
实时代码
但是,如果data
是非引用,则模板参数类型推导规则,如果参数的类型是数组类型(例如int[2][2]
),数组类型应"衰减"为其相应的指针类型,从而使int[2][2]
int(*)[2]
(如果参数const
则加const
)(由@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]
的引用(记住取消引用这个词)。因此,它也tmp
的类型。通过使用decltype(*i)
保留了原始类型。
但是,当您这样做时
auto tmp = *i;
猜猜decltype(tmp)
是什么:int*
!为什么?因为上面所有的胡说八道,还有一些模板魔法。
那么,为什么会出现错误int*
?因为std::begin
需要数组类型,而不是其较小的衰减指针。因此,auto j = std::begin(tmp)
会在tmp
int*
时导致错误。
如何解决(也TL;博士)?
保持原样。使用
decltype
.你猜怎么着。让您的
auto
变量成为参考!auto& tmp = *i;
实时代码
或
const auto& tmp = *i;
如果您不打算修改
tmp
的内容。(乔恩·珀迪的伟大)
故事的寓意:一个伟大的评论可以节省一个人千言万语。
更新:向decltype(i)
和decltype(*i)
给出的类型添加了const
,因为std::begin(data)
会返回一个const
指针,因为data
也被const
(由 litb 修复,谢谢)
- 是否可以将函数导入命名空间,但不能导出它?
- std::unique_ptr 在 GCC 中工作,但不能在 Visual Studio 中编译
- 声明C++具有动态大小的数组类型在 Linux 中工作正常,但不能在 Windows 中工作
- 为什么我可以隐式地将字符*转换为常量字符*,但不能将无符号字符*
- std::chrono::d uration 可以按秒初始化,但不能按毫秒初始化?
- Python/C++:可以导入犰狳(arma::)但不能导入子程序arma::arma_rng::randn
- 为什么我可以从C++文件中读取 int 值,但不能浮点?
- C++程序在将 int 与 cin 一起使用时有效,但不能使用字符串
- SqLite c++,可以创建数据库,但不能将 anthing 插入到表中
- std::remove() 按预期处理文字,但不能与取消引用的迭代器一起工作
- 16 位系统中的程序如何访问大于 65535 的整数,但不能访问地址
- C++:返回本地对象,但不能正常工作
- 可以访问一个类中的播放器结构,但不能访问另一个类中的播放器结构
- 返回实例变量的c++方法可以访问变量中的数据,但不能更改它,但在编译时不会生成错误
- 用C++替换std::字符串中的一个子字符串,但不能全部替换
- 为什么 decltype 在这里有效但不能自动?
- SDL_DISPLAYFORMAT有效,但不能进行SDL_DISPLAYFORMATALPHA
- 设置为实例的静态值在从静态方法设置时有效,但不能从类内的静态方法设置
- cin.get() 有效,但不能使用 cin.getline().我对cin.getline()做错了什么或误解了什么
- 为什么检查分配操作的结果有效,但不能与其他条件相结合