2014-02-17 65 views
13

我有如下代码:为什么decltype在这里工作,但不是汽车?

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; 
} 

如果我改变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, 

这究竟是为什么?

+14

'decltype'产量' int(&)[2]',而普通的'auto'强制指针转换(与模板参数推导相同的规则)。只需使用'auto&'。 – Xeo

+2

@Xeo这应该是一个答案。 – Angew

+0

@Xeo你能回答吗?你也可以指出一个解释这个扣除规则的URL吗? – texasbruce

回答

15

答案总结-up的一个评论:

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


@ XEO的评论,回答基本上说,因为auto涉及相同的规则template argument type deductionauto推导指针(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); 

你可以看到,dataconst T&类型,到const T的引用。现在,它正在通过arr,其类型为int[2][2],它是由两个int s(whoo!)组成的两个数组的阵列。现在来模板参数类型演绎。在这种情况下,它规定用data参考,T应该推断原始类型的参数,即int[2][2]。然后,它适用任何资格参数类型到参数,并用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); 

LIVE CODE

然而,如果data本来是非参考,模板参数类型推导规则,如果参数的类型是一个数组类型(例如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]的引用(请记住这个词解引用)。因此,它也是tmp的类型。 您通过使用decltype(*i)保留了原始类型。

但是,当你做

auto tmp = *i; 

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

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

如何解决(还有tl; dr)?

  • 保持原样。使用decltype

  • 猜猜看是什么。让您的auto ed变量成为参考!

    auto& tmp = *i; 
    

    LIVE CODE

    const auto& tmp = *i; 
    

    ,如果你不打算修改的tmp内容。 (Greatness by Jon Purdy


这个故事告诉我们:伟大的意见可以节省一个人千言万语。


UPDATE:加入constdecltype(i)decltype(*i)给出的类型,将std::begin(data)一个const指针返回由于data也正在constfix by litb,感谢)

+2

'const auto&'会更好。 –

+0

仍然,你能指出我在哪里在C++ 11规范说汽车强制指针转换? – texasbruce

+0

@JonPurdy'auto&'就足够了,因为如果源也是'const',它也会推导出'const',但是,如果你不打算修改你的变量,这会更好。 –

相关问题