【问题标题】:std::basic_string full specialization (g++ conflict)std::basic_string 完全专业化(g++ 冲突)
【发布时间】:2010-04-23 21:05:43
【问题描述】:

我正在尝试通过 <string> 标头定义 std::basic_string< char, char_traits<char>, allocator<char> > 的完整专业化(在 g++ 中)。

问题是,如果我首先包含 <string>,g++ 会将 typedef 视为 basic_string 的实例化并给我错误。如果我先做我的专业,那么我没有问题。

在包含<string> 之后,我应该能够定义我的专业。我需要做什么才能做到这一点?

我的代码:

#include <bits/localefwd.h>

//#include <string> // <- uncommenting this line causes compilation to fail

namespace std {
template<>
class basic_string< char, char_traits<char>, allocator<char> >
{
public:
    int blah() { return 42; }
    size_t size() { return 0; }
    const char *c_str() { return ""; }
    void reserve(int) {}
    void clear() {}
};
}

#include <string>
#include <iostream>

int main() {
    std::cout << std::string().blah() << std::endl;
}

上面的代码工作正常。但是,如果我取消注释第一行 #include &lt;string&gt;,我会收到以下编译器错误:

blah.cpp:7: error: specialization of ‘std::basic_string<char, std::char_traits<char>, std::allocator<char> >’ after instantiation
blah.cpp:7: error: redefinition of ‘class std::basic_string<char, std::char_traits<char>, std::allocator<char> >’
/usr/include/c++/4.4/bits/stringfwd.h:52: error: previous definition of ‘class std::basic_string<char, std::char_traits<char>, std::allocator<char> >’
blah.cpp: In function ‘int main()’:
blah.cpp:22: error: ‘class std::string’ has no member named ‘blah’

/usr/include/c++/4.4/bits/stringfwd.h 的第 52 行:

  template<typename _CharT, typename _Traits = char_traits<_CharT>,
           typename _Alloc = allocator<_CharT> >
    class basic_string;

据我所知,这只是模板的前向声明,而不是 g++ 声称的实例化。

/usr/include/c++/4.4/bits/stringfwd.h 的第 56 行:

   typedef basic_string<char>    string;

据我所知,这只是一个 typedef,也不是实例化。

那么为什么这些行与我的代码冲突?除了确保我的代码始终&lt;string&gt; 之前包含之外,我还能做些什么来解决这个问题?

【问题讨论】:

  • 给我们更大的图景,这样你就可以避免这场灾难:/
  • 顺便说一句:“据我所知,这只是模板的前向声明” - 它不是。通过使用extern template(在 C++03 中是非标准但被广泛接受的扩展,在 C++0x 中是标准的)来实现将在其他地方实例化的模板的前向声明。没有extern,它是一个“显式模板实例化”,如 ISO C++03 14.7.2 中所述
  • @GMan:我真正想要的是玩弄不同的字符串策略(如 COW)。显然,我可以创建自己的课程,但我希望因为它旨在成为 std::string 的直接替代品,它实际上会插入并替换它,甚至不需要任何代码修改,除了额外的包含。
  • @GMan,Soap:看看这个:drdobbs.com/cpp/…
  • @Pavel,不正确。他展示的实际上是一个模板的前向声明。你展示的(“外部模板”)是一个显式的实例化声明(并且将在 C++0x 中)。前向声明并不是要停止实例化,而是要在事实之前引入名称。这就是他展示的代码所做的,这就是“外部模板”所做的不是

标签: c++ templates stl g++ specialization


【解决方案1】:

只有当特化依赖于具有外部链接的用户定义名称时,才允许特化标准库。 char 不满足此要求,您将获得未定义的行为。

这是在 17.4.3.1 [lib.reserver.names]/1 中指定的。

您遇到的特定错误是因为您的实现已经实例化了您尝试专门化的模板,并且如果您为任何模板提供专门化,它必须在模板被实例化之前使用您的参数想提供专业化。

14.7.3 [temp.expl.spec]/6

【讨论】:

  • ISO C++03 17.4.3.1 “保留名称”:除非另有说明,否则 C++ 程序将声明或定义添加到命名空间 std 或命名空间 std 内的命名空间是未定义的。程序可以将任何标准库模板的模板特化添加到命名空间 std。标准库模板的这种特化(完全或部分)会导致未定义的行为,除非声明依赖于用户定义的外部链接名称,并且除非特化满足原始模板的标准库要求。)
【解决方案2】:

作为一般规则,您不应该在命名空间 std 中定义或专门化除特征类之外的任何内容,并且您当然不应该尝试更改 std::string 的底层类型的定义。

您应该在自己的命名空间中创建自己的字符数据类型,而不是专门化 basic_string,然后您应该为该类型专门化 std::char_traits(但命名空间 std 中没有其他内容),然后您应该为 basic_string 创建一个方便的 typedef在您自己的命名空间中使用这些类型进行实例化(不在命名空间 std 中,它属于 C++ 标准,用户不应更改)。

【讨论】:

  • 只要遵守所有规则,在std 中专门化现有模板是完全合法的。
  • 创建自己的 char 类型和自己的字符串与专门化 std::string 的目的完全不同(巧妙地改变它的行为)。
  • @SoapBox,可能是这样,但是修改标准容器,即使允许,仍然是一件可怕的事情,并且会让其他开发人员感到非常困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 2018-10-08
  • 1970-01-01
  • 2011-01-18
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
相关资源
最近更新 更多