18

我只是在尝试新的尾随返回类型,但我遇到了这个(简化的)代码的问题

#include <list>

class MyContainer{
  std::list<int> ints;

  auto begin( ) -> decltype(ints.begin())
  {
    return ints.begin();
  }

  auto begin( ) const -> decltype(ints.begin())
  {
    return ints.begin();
  }
};

忽略这段代码毫无意义的事实。重要的部分是使用 GCC 4.6.1(带-std=c++0x标志)时产生的编译器错误:

In member function 'std::list<int>::iterator MyContainer::begin() const':
error: could not convert '((const MyContainer*)this)->MyContainer::ints.std::list<_Tp, _Alloc>::begin [with _Tp = int, _Alloc = std::allocator<int>, std::list<_Tp, _Alloc>::const_iterator = std::_List_const_iterator<int>]()' from 'std::list<int>::const_iterator {aka std::_List_const_iterator<int>}' to 'std::list<int>::iterator {aka std::_List_iterator<int>}'

如果您不喜欢涉及模板的错误,短篇小说是在const版本的主体中MyContainer::begin,表达式ints.begin()返回一个类型的值std::list<int>::const_iterator(因为intsconst这样的上下文中)。但是,decltype(ints.begin())产生 type std::list<int>::iterator,即在决定表达式的类型时decltype 忽略方法的const限定符。begin不出所料,结果是类型冲突。

在我看来,这似乎是 GCC 编译器中的一个错误。decltype只有尊重const限定符并产生const_iterator类型才有意义。任何人都可以确认或否认(甚至可能解释)这一点吗?也许我忽略了 的机制中的某些内容decltype,但这看起来是一个非常简单的场景。

注意:据我所知,相同的行为不仅适用于,而且适用于任何具有在-ness 上std::list<int>重载的返回不兼容类型的成员函数的类型。const

4

1 回答 1

11

你是对的,这是一个错误。根据 N3291 第 5.1.1 节第 3 段:

如果声明声明类 X 的成员函数或成员函数模板,则表达式 this 是可选 cv-qualifer-seq 和函数定义结尾之间的“指向 cv-qualifier-seq X 的指针”类型的纯右值、成员声明符或声明符。它不应出现在可选的 cv-qualifier-seq 之前,并且不应出现在静态成员函数的声明中(尽管它的类型和值类别在静态成员函数中定义,因为它们在非静态成员函数中) . [注意:这是因为在知道完整的声明符之前不会发生声明匹配。—尾注] 与其他上下文中的对象表达式不同,*this 不需要是完整类型,以便在成员函数体之外访问类成员(5.2.5)。[笔记:只有在声明之前声明的类成员是可见的。——尾注]

但这是最近的工作草案和 N3291 之间的变化。所以 GCC 在不到 6 个月前是正确的。这就是根据动态规范编写代码的危险。

于 2011-08-31T10:26:28.457 回答