Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.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
为什么C++;标准指定模板中的非限定名称是非依赖的吗? 为什么C++标准指定模板中的非限定名称是非依赖的?< /p>_C++_Templates - Fatal编程技术网

为什么C++;标准指定模板中的非限定名称是非依赖的吗? 为什么C++标准指定模板中的非限定名称是非依赖的?< /p>

为什么C++;标准指定模板中的非限定名称是非依赖的吗? 为什么C++标准指定模板中的非限定名称是非依赖的?< /p>,c++,templates,C++,Templates,e、 g 模板 阶级基础 { 公众: tx; }; 模板 丙级:公共基地 { 公众: bool m(){return x==0;}//错误:未声明的标识符“x” }; 引用SO的公认答案中关于如何克服限制的内容: 该标准规定模板中的非限定名称是 非依赖性,在定义模板时必须查找。这个 依赖基类的定义在当时是未知的 (基类模板的专门化可能存在)因此不合格 无法解析名称 但是,引用的答案和其他答案没有说明标准规定的原因。这种限制的理由是什么?这与标准实际所说的并不完全一致。它实际上说的是,在非限定名

e、 g

模板
阶级基础
{
公众:
tx;
};
模板
丙级:公共基地
{
公众:
bool m(){return x==0;}//错误:未声明的标识符“x”
};
引用SO的公认答案中关于如何克服限制的内容:

该标准规定模板中的非限定名称是 非依赖性,在定义模板时必须查找。这个 依赖基类的定义在当时是未知的 (基类模板的专门化可能存在)因此不合格 无法解析名称


但是,引用的答案和其他答案没有说明标准规定的原因。这种限制的理由是什么?

这与标准实际所说的并不完全一致。它实际上说的是,在非限定名称查找期间,不检查依赖基类。一个非限定名称当然可以依赖于正确的上下文-这就是模板中ADL的工作原理:给定一个模板类型参数
T
,则
foo(T())
中的
foo
是一个依赖名称


无论如何,它不能进行这种查找的原因很简单:在模板定义时,由于以后可能会出现专门化,您不知道依赖基类将是什么样子,因此无法进行任何有意义的查找。模板中每个拼写错误的标识符都有一个依赖的基类,只有在实例化时才能被诊断出来(如果有的话)。由于每个非限定标识符都可能来自依赖的基类,因此您需要某种方法来回答“这是一个类型吗?”和“这是一个模板吗?”,因为这些问题的答案会影响解析。这意味着类似可怕的
typename
template
关键字,但在更多的地方。我认为这是一个一致性问题。考虑这个,有点修改的例子:

头文件:

template<typename T>
class Base
{
public:
    T x;
};

extern int x;

template<typename T>
class C : public Base<T>
{
public:
    bool m() { return x == 0; }
};
模板
阶级基础
{
公众:
tx;
};
外部INTX;
模板
丙级:公共基地
{
公众:
bool m(){return x==0;}
};
和源文件:

template<>
class Base<int> // but could be anything
{
public:
    // no x here
};

int main()
{
  C<char> c1;
  c1.m(); // may select ::x or Base<char>::x
  C<int> c2;
  c2.m(); // has only one choice: ::x
  return(0);
}
模板
类基类//但可以是任何对象
{
公众:
//这里没有x
};
int main()
{
c1;
c1.m();//可以选择::x或Base::x
c2;
c2.m();//只有一个选择:::x
返回(0);
}
标准中的措辞保证编译器要么会出错,要么会选择它在模板定义点看到的任何符号。如果编译器将名称解析推迟到模板实例化,那么它可能会选择此时可见的不同对象,这可能会让开发人员感到惊讶

如果开发人员想要访问依赖名称,他必须明确地声明这一点,并且不应该让他失去警惕

还请注意,如果x在模板定义点不可用,则以下代码将(意外地)破坏一个定义规则:

1.cpp

#include <template_header>

namespace {
int x;
}

void f1()
{
  C<int> c1;
}
#包括
名称空间{
int x;
}
void f1()
{
c1;
}
2.cpp

#include <template_header>

namespace {
char x;
}

void f2()
{
  C<int> c2;
}
#包括
名称空间{
字符x;
}
无效f2()
{
c2;
}

我们希望c1和c2变量的类型相同,例如可以安全地传递给采用
C const&
参数的函数,但基本上这两个变量的类型名称相同,但实现方式不同。

在Visual Studio 2015中,无论是否符合标准,这都不是问题。此外,尽管有来自Tomek的注释,编译器还是采用了正确的x

static int x = 42;

template<typename T>
class Base {
public:
    T x;
    Base() { x = 43; }
};

template<>
class Base<int> { };

template<typename T>
class C :public Base<T>
{
public:
    int m() { return x; }
};

void main() {
    C<int> cint;
    C<char> cchar;

    std::cout << "cint: " << cint.m() << std::endl;
    std::cout << "cchar: " << cchar.m() << std::endl;
}

// Output is:
// cint: 42
// cchar: 43
static int x=42;
模板
阶级基础{
公众:
tx;
Base(){x=43;}
};
模板
类基{};
模板
丙级:公共基地
{
公众:
int m(){return x;}
};
void main(){
C辛特;
C cchar;

std::cout但是通过在类范围中使用T::x
添加
,限制消失了,即使可能存在使
x
无效的专门化(在这种情况下,我想实例化会给出一个错误)。因此,我假设以类似的方式,在编译器第二次遍历模板时,它可以自己使用
T::x
,或者发现非限定名称
x
无效。@Danra是的,它可以在实例化时找到它。问题是编译器无法在模板de时进行任何打字检查定义时间,因为每一个输入错误都可能是一个依赖的基成员。这是一个很好的观点。也许这就是它背后的基本原理。不过,理论上,我想如果编译器和它们运行的机器足够快,代码编辑器可以实时完整地编译所有代码,包括模板实例化,那么输入错误捕获可能会发生“不要推迟到那个阶段。@模板代码的作者Danra可能不是实例化模板的代码的作者。(只考虑页眉模板库。)我们总是可以在这个交替的宇宙中制定一条规则,即我们在依赖基中发现的任何东西,比如说,依赖基的主模板是
x
在实例化时绑定到的。我相信这将打破全部和部分模板专门化。一旦MSVC的模板处理达到C++98标准,这很有可能会打破ds兼容:)(恐怕它还不在VC++2017中,请参见示例)
static int x = 42;

template<typename T>
class Base {
public:
    T x;
    Base() { x = 43; }
};

template<>
class Base<int> { };

template<typename T>
class C :public Base<T>
{
public:
    int m() { return x; }
};

void main() {
    C<int> cint;
    C<char> cchar;

    std::cout << "cint: " << cint.m() << std::endl;
    std::cout << "cchar: " << cchar.m() << std::endl;
}

// Output is:
// cint: 42
// cchar: 43