为什么decltype在这里工作但不是自动?

tex*_*uce 13 c++ c++11

我有以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

如果我更改decltypeauto,它将无法编译和抱怨(部分错误):

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,
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

Mar*_*cia 15

答案总结为一条评论:

decltype产生int(&)[2],而普通auto强制指针转换(与模板参数推导相同的规则).只是用auto&.- Xeo


@ XEO的评论,回答基本上说,因为auto涉及相同的规则模板参数类型推导,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);
Run Code Online (Sandbox Code Playgroud)

你可以看到它data的类型const T&,对a的引用const T.现在,它正在传递arr,其类型是int[2][2],两个ints 的两个数组(whoo!)的数组.现在来模板参数类型推导.在这种情况下,它规定data作为参考,T应该用参数的原始类型推导出来,即int[2][2].然后,它适用任何资格参数类型的参数,并用data'S是限定类型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);
Run Code Online (Sandbox Code Playgroud)

现场代码

然而,如果 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.
Run Code Online (Sandbox Code Playgroud)

现在,你的代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

战斗前的一些先决条件:

  • 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;
Run Code Online (Sandbox Code Playgroud)

猜猜是什么decltype(tmp):int*!为什么?因为所有的blabbery-blablablah上面,还有一些模板魔法.

那么,为什么错误int*呢?因为std::begin需要一个数组类型,而不是它的较小的衰减指针.因此,auto j = std::begin(tmp)当会导致错误tmpint*.

如何解决(也是tl; dr)?


故事的道德:一个伟大的评论为一个人节省了千言万语.


更新:加入const到由给定的类型decltype(i)decltype(*i),作为std::begin(data)将返回一个const由于指针data也被const(通过litb修复,感谢)

  • `const auto&`会更好. (2认同)
  • `int [2] [2]`衰变为`int(*)[2]`,而不是指向指针的指针. (2认同)