Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
字符串文字不允许作为非类型模板参数 以下引用由Addison Wesley的C++模板提供。有人能帮我用通俗易懂的英语/外行术语理解它的要点吗_C++_Templates_String Literals - Fatal编程技术网

字符串文字不允许作为非类型模板参数 以下引用由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"