为什么编译器会尝试实例化一个我实际上没有在任何地方实例化的模板?

15
以下是我在main.cpp中的全部代码:

更新如下

template<class T>
struct other_traits;

template<class T>
struct some_traits{
    typedef decltype(&T::operator()) Fty;
    typedef typename other_traits<Fty>::type type;
};

int main(){
}

但是,当使用Visual Studio 2010编译时,我会遇到以下错误,而g++可以正常编译

src\main.cpp(9): error C2146: 语法错误:在标识符“type”之前缺少“;”
--src\main.cpp(10) : 参见正在编译的类模板实例化“some_traits<T>
src\main.cpp(9): error C2868: 'some_traits<T>::type' : 使用声明的语法非法;应为限定名

(我喜欢最后一个,完全不知所云。)

我能认为这是VC10的一个bug吗?还是有早期实例化的充分理由?或者是decltype的一个bug使得编译器认为Fty不是依赖名称?


更新:我尝试通过继承一个基类来欺骗编译器,让它认为Fty是一个依赖名称:

template<class T>
struct other_traits;

template<class R, class C>
struct other_traits<R (C::*)()>{
    typedef R type;
};

template<class Fty>
struct base_traits{
    typedef typename other_traits<Fty>::type type;
};

template<class T>
struct some_traits
    : public base_traits<decltype(&T::operator())>
{};

但编译器仍试图立即实例化/编译所有内容,并输出这些错误:
src\main.cpp(13): error C2039: 'type' : is not a member of 'other_traits<T>'
          with
          [
              T=
          ]
          src\main.cpp(19) : see reference to class template instantiation 'base_traits<Fty>' being compiled
          with
          [
              Fty=
          ]
          src\main.cpp(19) : see reference to class template instantiation 'some_traits<T>' being compiled
src\main.cpp(13): error C2146: syntax error : missing ';' before identifier 'type'
src\main.cpp(13): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\main.cpp(13): error C2602: 'base_traits<Fty>::type' is not a member of a base class of 'base_traits<Fty>'
          with
          [
              Fty=
          ]
          src\main.cpp(13) : see declaration of 'base_traits<Fty>::type'
          with
          [
              Fty=
          ]
src\main.cpp(13): error C2868: 'base_traits<Fty>::type' : illegal syntax for using-declaration; expected qualified-name
          with
          [
              Fty=
          ]

请注意,模板参数是空的。有什么想法吗?

尝试将其重写为typedef typename other_traits<typename Fty>::type type;(只是一次盲猜)。 - Šimon Tóth
1
随机猜测 - 尝试在该行中添加更多的 "typename" 关键字(和括号),以指示 Ftyother_traits<Fty>other_traits<Fty>::type 都是类型。看起来 VC++ 在实例化之前进行了一些错误检查(在某种程度上是允许的),但可能因为使用了 "decltype" 而感到困惑。 - user180247
@stjin:嗯,显然那是错误的……它不是一个依赖名称。尝试将 decltype(...) 替换为简单的 T - Xeo
如果有帮助的话,codepad(gcc 4.1.2)会给出以下信息:“第6行:错误:在'&'标记之前需要标识符\n编译由于-Wfatal-errors而终止。” - John McFarlane
1
@Johannes:有趣,是吧?如果我将 decltype(...) 作为模板参数传递给基类,那么这个问题就可以解决。我会尽快尝试的。 - Xeo
显示剩余5条评论
4个回答

2

如果没有设置特殊标志,似乎存在一个 错误。 以下是来自Oracle网站关于C++模板的摘录:

7.2.2

ISO C++标准允许开发人员编写的模板类中可能包含不合法的成员,但只要这些非法成员没有被实例化,程序仍然是良构的。ISO C++标准库就使用了这种技术。但是,当使用有问题的模板参数实例化此类模板类时,-template=wholeclass选项会实例化所有成员,因此不能与这样的模板类一起使用。


这一事实得到了Comeau在线编译器的支持,该编译器认为原始代码是完全合法的。 - rotoglup

1

我认为你遇到了一个与编译器在看到decltype时过早实例化相关的错误。


0

你在这里做了一个技术上不正确的假设。让我们先解决这个问题。

你假设语法错误意味着模板被实例化。这不是模板应该被编译的方式。模板在实例化之前首先被编译。这是查找非相关名称的阶段。语法错误肯定可以在此阶段中发现。例如,任何必须以声明结束而不考虑模板参数的内容都必须以;结尾。

现在正确的问题是编译器是否正确地考虑了other_traits<T>的特化。当然,在第9行之前没有这样的特化,尽管以后可能会有特化。但是对于那些来说,将是相关的实例化点吗?我得查一下(抱歉,AFS Away From Standard)。如果other_traits<T>是第9行,那么就没有特化,而other_traits<Fty>::type是无效的。如果other_traits<Fty>::type的实例化点与some_traits<T>的实例化点相同,即没有,则应在第1阶段接受other_traits<Fty>::type


好的,那么为什么编译器告诉我它实例化了模板呢?:| 当我使用依赖名称时,为什么编译甚至相关呢?它应该延迟查找。即使有部分特化,编译器怎么知道选择哪一个?Fty取决于模板参数,因此它是一个依赖名称,至少在我理解的范围内是这样的。 - Xeo
1
好吧,它确实是MSVC,这是一个以不正确执行两阶段实例化而闻名的编译器。回到理论上来说,编译器需要至少解析您的代码,以确定某些内容是否实际上是一个相关名称;在那之前你就能有语法错误了。例如:typedef: x - 你不能弄清楚x是否是有关的; :已经是错的(::x不会是相关的)。 - MSalters

0
template<class T>
struct other_traits; // <-- I don't see a "type" attribute in this struct

template<class T>
struct some_traits{
    typedef decltype(&T::operator()) Fty;
    typedef typename other_traits<Fty>::type type; // <-- Here you are trying to access other_traits<T>::type which doesn't exist
};

int main(){
}

你看不到它,因为这个结构体是不透明的。 - alternative
我不确定我在这里理解你的意思,"opaque" 的意思是内部细节在另一个翻译单元/头文件中。发起人说他的所有代码都在单个文件 main.cpp 中,因此没有其他地方定义结构体。 - David
1
它甚至不应该尝试访问other_traits,因为Fty是依赖于T的名称。 - Xeo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接