【问题标题】:"Explicit specialization of std::iterator_traits<char *> after instantiation" (CLang)“实例化后 std::iterator_traits<char *> 的显式特化”(CLang)
【发布时间】:2012-09-25 18:30:22
【问题描述】:

我有一些代码在 MSVC 下编译得很好(或者说是发送给我的 Windows 开发人员),但在 CLang 下会出错。环顾四周后,我发现 CLang 在解决模板专业化方面确实更加严格,但我不确定我应该将专业化放在哪里。基本上我的一个文件有这样的结构:

template<>
struct iterator_traits< char * >   // error is here
{
    typedef random_access_iterator_tag iterator_category;
    typedef char value_type;
    typedef ptrdiff_t difference_type;
    typedef difference_type distance_type;
    typedef char * pointer;
    typedef char & reference;
};

这是在 namespace std 块内。错误信息是:

Explicit specialization of 'std::iterator_traits<char *>' after instantiation

同一错误消息的另一部分(通过在 Xcode 中“扩展”错误消息查看)显示 Implicit instantiation first required here,点击它会将我带到 stl_iterator.h,特别是这一行(第 642 行):

typedef typename iterator_traits<_Iterator>::iterator_category
                                                         iterator_category;

有谁知道在这种情况下正确的做法是什么?我见过涉及类的示例,但从未见过涉及结构的示例。

【问题讨论】:

  • 这看起来像标准库代码。你是不是偶然尝试用 Clang 编译 MS 标准库?
  • 检查您是否包含标准库的正确标题。除此之外,structclass 几乎可以互换(唯一的区别是struct 中的默认访问说明符是public)
  • @KerrekSB 将来自std 的东西专门化,例如用于自定义迭代器的std::iterator_traits,在标准库之外非常普遍(即使还有其他方法,例如从std::iterator 派生)。尽管为char* 执行此操作仍然很奇怪,因为标准库已经为任何T* 提供了一个。
  • @ChristianRau:我很确定您只能将来自std 的模板专门用于用户定义的类型
  • @KerrekSB 是的,看起来(在阅读了亚当的回答之后)。我只是指出这不一定看起来像标准库代码。

标签: c++ xcode macos clang template-specialization


【解决方案1】:

编译器抱怨您在实例化泛型模板后试图特化模板——到那时,编译器已经使用泛型模板进行实例化,它无法返回并使用而是你的专业。换句话说,是这样的:

template <typename T>
struct X
{
    // Generic implementation
};

// Instantiate template by using it in any way
X<int> foo;

template<>
struct X<int>
{
    // Specialization implementation for int
};

解决方法是在实例化之前定义特化,因此在本例中,您需要将 X&lt;int&gt; 特化移到使用 X&lt;int&gt; 的之前。

请注意,STL 已经为指针类型定义了 std::iterator_trait 的特化,因此无需在此处为 char* 定义您自己的特化。您通常只对不是指针的用户定义迭代器类型执行此操作。请参阅 C++03 标准的 §24.3.1/2:

[模板iterator_traits&lt;Iterator&gt;]专门用于指针作为

template<class T> struct iterator_traits<T*> {
  typedef ptrdiff_t difference_type;
  typedef T value_type;
  typedef T* pointer;
  typedef T& reference;
  typedef random_access_iterator_tag iterator_category;
};

对于指向const 的指针

template<class T> struct iterator_traits<const T*> {
  typedef ptrdiff_t difference_type;
  typedef T value_type;
  typedef const T* pointer;
  typedef const T& reference;
  typedef random_access_iterator_tag iterator_category;
};

因此,提供您自己的std::iterator_traits&lt;char*&gt; 专业化是没有意义的。由于char* 不是用户定义的类型,因此根据标准它也是未定义的行为。 §17.4.3.1/1 说:

C++ 程序向命名空间 std 或命名空间添加声明或定义是未定义的 在命名空间std 内,除非另有说明。程序可以为任何 标准库模板到命名空间std。标准的这种专业化(完全或部分) 库模板会导致未定义的行为,除非声明依赖于用户定义的名称 外部链接,除非专业化满足原始模板的标准库要求。163)

163) 任何实例化其他库模板的库代码都必须准备好与任何用户提供的专业化充分合作 符合标准的最低要求

【讨论】:

  • 我很确定你可以从标准库中专门化一些东西。
  • @PeterAlexander:我很确定专门为用户定义的类型是你在std命名空间中唯一被允许做的事情。
  • 我同意@DavidRodríguez-dribeas。更准确地说:17.4.3.1“程序可以将任何标准库模板的模板特化添加到命名空间std。标准库模板的这种特化(完整或部分)会导致未定义的行为,除非声明依赖于用户定义的名称外部链接,除非专业化满足原始模板的标准库要求。”
  • @DavidRodríguez-dribeas ... 从原始问题 UB 生成代码。
  • @Robᵩ:哦,对了,我误解了代码在实现的不同标头中,而不是用户标头中。你是对的,这是未定义的行为,你能得到的最好的结果是编译器错误......另一种选择是违反 ODR,这将更难调试
猜你喜欢
  • 2011-12-08
  • 1970-01-01
  • 1970-01-01
  • 2011-12-17
  • 1970-01-01
  • 2018-06-07
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多