字符串文字不允许作为非类型模板参数 以下引用由Addison Wesley的C++模板提供。有人能帮我用通俗易懂的英语/外行术语理解它的要点吗
由于字符串文字是具有内部链接的对象(具有相同值但在不同模块中的两个字符串文字是不同的对象),因此也不能将它们用作模板参数:字符串文字不允许作为非类型模板参数 以下引用由Addison Wesley的C++模板提供。有人能帮我用通俗易懂的英语/外行术语理解它的要点吗,c++,templates,string-literals,C++,Templates,String Literals,由于字符串文字是具有内部链接的对象(具有相同值但在不同模块中的两个字符串文字是不同的对象),因此也不能将它们用作模板参数: 这意味着你不能这么做 #include <iostream> template <const char* P> void f() { std::cout << P << '\n'; } int main() { f<"hello there">(); } 您的编译器最终会对调用的对象进行操作。在这些转
这意味着你不能这么做
#include <iostream>
template <const char* P>
void f() { std::cout << P << '\n'; }
int main()
{
f<"hello there">();
}
您的编译器最终会对调用的对象进行操作。在这些转换单元中,您可以标识不同的实体:对象、函数等。链接器的任务是将这些单元连接在一起,而该过程的一部分是合并标识 标识符具有链接†:内部链接表示该翻译单元中命名的实体仅对该翻译单元可见,而外部链接表示该实体对其他单元可见 当一个实体被标记为静态时,它被赋予内部链接。考虑到这两个翻译单元:
// a.cpp
static void foo() { /* in a */ }
// b.cpp
static void foo() { /* in a */ }
这些foo
中的每一个都是指仅对其各自的翻译单元可见的实体(本例中的函数);也就是说,每个翻译单元都有自己的foo
下面是一个要点:字符串文本的类型与静态常量char[…]
相同。即:
// str.cpp
#include <iostream>
// this code:
void bar()
{
std::cout << "abc" << std::endl;
}
// is conceptually equivalent to:
static const char[4] __literal0 = {'a', 'b', 'c', 0};
void bar()
{
std::cout << __literal0 << std::endl;
}
因为每个翻译单元的“abc”是不同的。每个翻译单元将被赋予不同的类,因为每个“abc”
都是不同的实体,即使它们提供了“相同”的参数
在语言层面上,这是因为模板非类型参数可以是指向具有外部链接的实体的指针;也就是说,跨翻译单元引用同一实体的事物
所以这很好:
// good.hpp
extern const char* my_string;
// good.cpp
const char* my_string = "any string";
// anything.cpp
typedef baz<my_string> coherent; // okay; all instantiations use the same entity
//good.hpp
外部常量字符*我的字符串;
//好的
const char*my_string=“任意字符串”;
//任何事
typedef baz相干;//可以所有实例化都使用相同的实体
†并非所有标识符都有链接;有些没有,例如函数参数
‡优化编译器将在同一地址存储相同的文本,以节省空间;但这只是实现细节的质量,而不是保证。显然,像“foobar”这样的字符串文本与其他文本内置类型(如int或float)不同。它们需要有一个地址(const char*)。地址实际上是编译器用来代替文本出现位置的常量值。该地址指向程序内存中某个在编译时固定的位置 因此,它必须具有内部联系。内部链接只是指不能跨翻译单元(编译的cpp文件)链接。编译器可以尝试这样做,但不需要这样做。换句话说,内部链接意味着,如果您在不同的cpp文件中获取两个相同的文本字符串(即它们转换为的常量字符*的值)的地址,那么它们通常不会相同 不能将它们用作模板参数,因为它们需要strcmp()来检查它们是否相同。如果使用==,则只需比较地址,当模板在不同的翻译单元中使用相同的文本字符串实例化时,地址就不同了
其他更简单的内置类型,如文本,也是内部链接(它们没有标识符,不能从不同的翻译单元链接在一起)。然而,他们的比较是微不足道的,因为这是价值。因此,它们可以用于模板 如其他答案中所述,字符串文字不能用作模板参数。 不过,有一种变通方法具有类似的效果,但“字符串”仅限于四个字符。正如在链接中所讨论的,这可能是由于它不可移植,但出于调试目的
template<int32_t nFourCharName>
class NamedClass
{
std::string GetName(void) const
{
// Evil code to extract the four-character name:
const char cNamePart1 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*3) & 0xFF);
const char cNamePart2 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*2) & 0xFF);
const char cNamePart3 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*1) & 0xFF);
const char cNamePart4 = static_cast<char>(static_cast<uint32_t>(nFourCharName ) & 0xFF);
std::ostringstream ossName;
ossName << cNamePart1 << cNamePart2 << cNamePart3 << cNamePart4;
return ossName.str();
}
};
模板
类名类
{
std::string GetName(void)const
{
//提取四个字符名称的邪恶代码:
const char cNamePart1=static_cast(static_cast(nFourCharName>>8*3)&0xFF);
const char cNamePart2=static_cast(static_cast(nFourCharName>>8*2)&0xFF);
const char cNamePart3=static_cast(static_cast(nFourCharName>>8*1)&0xFF);
const char cNamePart4=static_cast(static_cast(nFourCharName)&0xFF);
std::ostringstream ossName;
C++标准的OSNox思想只允许某些类型的参数到模板,参数应该是常数,并且在编译时已知,以便生成“专用类”代码。
对于这一具体情况:
当您创建字符串文字时,它们的地址在链接时间之前是未知的(链接发生在编译之后),因为跨不同翻译单元的两个字符串文字是两个不同的对象(正如公认的答案所精辟地解释的)。编译时,我们不知道使用哪个字符串文字的地址从模板类生成专用的类代码。我不知道第二种形式是可能的,即使在当前的标准中也是如此?@Nim:是的,奇怪但正确:-)@Anisha:integer中的integral,1,2,3。模板参数必须是类型或整数;这就是为什么enums、characters、short/int/long等都可以,但float和double则不行,实际对象也不行。这里的要点是,具有编译时常量值的指针是排序的整数,“extern”变量在“static”时满足该要求变量-在当时编译的对象中被有效隐藏,因此没有整数地址“广播”供链接器与其他对象缝合在一起,因此不能使用。另请参见GMan的答案;-)在第二种形式中,p
extern char const p[]=”不需要两个声明
这个技巧做得很好。@Anisha:哦,他们确实有链接:-)…看不是“tr”
// good.hpp
extern const char* my_string;
// good.cpp
const char* my_string = "any string";
// anything.cpp
typedef baz<my_string> coherent; // okay; all instantiations use the same entity
template<int32_t nFourCharName>
class NamedClass
{
std::string GetName(void) const
{
// Evil code to extract the four-character name:
const char cNamePart1 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*3) & 0xFF);
const char cNamePart2 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*2) & 0xFF);
const char cNamePart3 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*1) & 0xFF);
const char cNamePart4 = static_cast<char>(static_cast<uint32_t>(nFourCharName ) & 0xFF);
std::ostringstream ossName;
ossName << cNamePart1 << cNamePart2 << cNamePart3 << cNamePart4;
return ossName.str();
}
};
NamedClass<'Greg'> greg;
NamedClass<'Fred'> fred;
std::cout << greg.GetName() << std::endl; // "Greg"
std::cout << fred.GetName() << std::endl; // "Fred"