6

我有一个继承自基类模板的类模板。

基类模板有一个带有成员函数模板的数据成员,我想从我的超类中调用它。

我知道,为了消除对成员函数模板的调用歧义,我必须使用关键字,并且必须在超类中template显式引用。this

this->base_member_obj.template member_function<int>();

所有这一切都很好,除了我正在使用的代码库犯了一个相当不幸的错误,即导入了整个namespace std,并且我试图调用的模板成员函数被调用set。包含在框架中的某处std::set,这导致 GCC 认为我正在尝试声明 astd::set而不是调用成员函数set

GCC 4.7 抛出错误invalid use of 'class std::set'

有关显示错误的示例,请参见下文。如果你注释掉using namespace std代码编译得很好。

可悲的是,我无法遍历整个代码库,删除每个using namespace std调用,并将每个调用前缀为 std 命名空间内的任何内容std::

有没有其他方法可以解决这个问题?

#include <set>
using namespace std; // comment this out to compile fine

struct blah
{
    template<typename T>
    void set()
    { }
};

template<typename T>
struct base
{
    blah b;
};

template<typename T>
struct super : base<super<T>>
{
    void fun()
    {
        this->b.template set<int>(); // this line breaks
    }
};

int main()
{
    super<int> s;
    s.fun();

    return 0;
}
4

3 回答 3

5

试试这个:

this->b.blah::template set<int>(); // this line breaks
于 2012-05-03T07:13:26.923 回答
3

好吧,这对我们 C++ 推动者来说是相当尴尬的。

这是 G++ 中的一个错误,它也出现在 Comeau Test Drive 中。这不是语言本身的缺陷。问题源于解析的从左到右的性质以及 C++ 语法避开歧义的方式。

在嵌套名称说明符中使用非成员、非基类模板到 typedef 到基类模板是合法的。在这种情况下,与被访问类没有特殊关系的类模板可以出现在->

#include <tuple>

template< typename t >
struct get_holder
    { typedef std::tuple< t > type; };

template< typename ... ts >
struct inherits
    : get_holder< ts >::type ... {

    inherits( ts ... v )
        : get_holder< ts >::type( v ) ...
        {}

    template< typename tn >
    void assign_one( inherits &o )
        { this->get_holder< tn >::type::operator= ( o ); } // <- here!
};

int main() {
    inherits< int, char, long > icl( 3, 'q', 2e8 );
    icl.assign_one< char >( icl );
}

由于 C++ 是从左到右解析的,因此当解析器命中 时->,它必须get_holder在继续之前解析。该标准为此有一个特殊条款 §3.4.5/1 [basic.lookup.classref]:

在类成员访问表达式 (5.2.5) 中,如果 . 或 -> 标记后紧跟一个标识符,后跟一个 <,必须查找标识符以确定 < 是模板参数列表 (14.2) 的开头还是小于运算符。标识符首先在对象表达式的类中查找。如果未找到标识符,则在整个后缀表达式的上下文中查找它并命名一个类模板。如果在对象表达式的类中查找找到了一个模板,该名称也会在整个后缀表达式的上下文中查找,并且

— 如果没有找到名称,则使用在对象表达式的类中找到的名称,否则

— 如果在整个后缀表达式的上下文中找到名称并且没有命名类模板,则使用在对象表达式的类中找到的名称,否则

如果找到的名称是类模板,则它应引用与在对象表达式的类中找到的实体相同的实体,否则程序是非良构的。

强调我的——尽管template关键字出现在.和标识符之间,但 G++ 似乎正在遵循这个逻辑set。此外,假设它沿着这条路线前进,它应该将歧义标记为错误,而不是尝试选择非成员。

template当关键字确实出现时,标准的措辞似乎确实存在缺陷,但它不应该引起您看到的混乱。§14.2 [临时名称]:

当成员模板专业化的名称出现在 . 或 -> 在后缀表达式中,或在限定 ID 中的嵌套名称说明符之后,并且后缀表达式或限定 ID 显式依赖于模板参数 (14.6.2) 但不引用当前实例化(14.6.2.1)的成员,成员模板名称必须以关键字模板为前缀。否则,该名称被假定为命名非模板。

强调我的,该文本是错误的,应该是“假定名称不命名成员模板”,因为在我上面的插图中,它可能是嵌套名称说明符的一部分。如果文本按原样进行,则可以将其解释为在我的插图template需要关键字,因此可以指示以下非成员模板(假设语言完全支持这种结构),然后是您的程序可能会被 G++ 误解。

但是设计意图很明确,您不需要添加人为的嵌套名称说明符blah::,尽管这是一种合法的解决方法。

于 2012-05-03T07:09:06.393 回答
3

你有两个选择:

    this->b.blah::template set<int>(); 
     //     ^^^^^^ Be very explicit what set you want

或者不要使用using namespace(最好的选择)。

于 2012-05-03T07:09:36.443 回答