模板对象的模板朋友功能和名称空间 [英] template object's template friend functions and namespaces

查看:53
本文介绍了模板对象的模板朋友功能和名称空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的C ++示例代码中,GCC 6和Clang 3.8在正确的行为上存在分歧:

In the following C++ example code, GCC 6 and Clang 3.8 disagree on what the correct behaviour is:

这个人为设计的示例有效"-如 test()函数中那样,在GCC中返回 o.p .在clang中,它调用(未定义的)函数 get< int,int,float,double> :

This contrived example "works" -- as in the test() function returns o.p in GCC. In clang, it calls the (undefined) function get<int, int, float, double>:

template<typename ...Args>
class obj {
 bool p = false;

 template<typename T, typename... Args2>
 friend T get(const obj<Args2...> &o) { return o.p; }
};

template<typename T, typename... Args>
T get(const obj<Args...> &o);


bool test(const obj<int, float, double> &a) {
 return get<int>(a);
}

在命名空间中放入相同的代码会使GCC进行clang所做的相同的事情.

Putting the same code in a namespace causes GCC to do the same thing clang does.

namespace ns {

template<typename ...Args>
class obj {
 bool p = false;

 template<typename T, typename... Args2>
 friend T get(const obj<Args2...> &o) { return o.p; }
};

template<typename T, typename... Args>
T get(const obj<Args...> &o);

}

bool test(const ns::obj<int, float, double> &a) {
 return ns::get<int>(a);
}

https://godbolt.org/g/sWrXQO 哪个编译器是正确的",并且通常有一种方法可以内联定义内联成员模板函数,而不必先声明它,然后单独定义它.也就是说,像这样的东西:

Which compiler is "correct" and is there in general a way to define a friend member template function inline without having to declare it and then define it separately. That is, things like:

struct Foo {
 friend bool bar() { return true; } // declares *and* defines a free function bar
 template<typename T> T bar2() { return true; }  // doesn't work!
};

推荐答案

关于在类模板中定义的 friend 功能模板有两个未解决的问题:

There are two unresolved issues regarding friend function templates defined in class templates: 1545 and 2174. The former questions the extent to which it's valid at all and the latter is about odr violations that may arise based on the actual instantiations of those function templates. I am unsure which compiler is right (having previously believed that both were wrong), but it may simply be under- or poorly specified in the standard what the correct behavior is in this situation.

代码应该理想地进行编译(待解决问题):

The code should ideally compile (pending issue resolution):

template<typename ...Args>
class obj {
 bool p = false;

 template<typename T, typename... Args2>
 friend T get(const obj<Args2...> &o) { return o.p; }
};

template<typename T, typename... Args>
T get(const obj<Args...> &o);

friend 声明首先声明 get ,因此这将创建最内层封闭名称空间的新成员: :: get .外部声明只是重新声明了相同的功能,因此实际上只有一个 :: get .来自[temp.over.link]:

The friend declaration first declares get, so this creates a new member of the innermost enclosing namespace: ::get. The external declaration just redeclares the same function, so there really is just the one ::get. From [temp.over.link]:

如果包含两个参数的两个函数定义包含模板参数,则两个表达式被认为是等效的该表达式将满足一定义规则(3.2),但用于命名该表达式的标记除外模板参数可能会有所不同,只要一个表达式中用于命名模板参数的标记是替换为在另一个表达式中命名相同模板参数的另一个标记.用于确定是否两个从属名称(14.6.2)等价,则仅考虑名称本身,而不考虑模板上下文中名称查找的结果.

Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one-definition rule (3.2), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression. For determining whether two dependent names (14.6.2) are equivalent, only the name itself is considered, not the result of name lookup in the context of the template.

使用不同的模板参数名称( Args ... Args2 ... )很好-函数模板 :: get <的第二个声明/code>是有效的,并允许查找以找到它.

Using different template parameter names (Args... vs Args2...) is fine - this second declaration of the function template ::get is valid and allows for lookup to find it.

这将我们带到:

bool test(const obj<int, float, double> &a) {
#ifdef UNQUAL
    return get<int>(a);     // unqualified
#else
    return ::get<int>(a);   // qualified
#endif
}

这两个都应该有效-由于我们重新声明了该函数在名称空间范围内,因此我们甚至不必担心ADL.两次调用都应找到 :: get< int>(obj< int,float,double>),它是 friend ,因此代码应编译并链接.gcc允许不合格的呼叫,但不允许合格的呼叫,clang不允许.

Both of these should work - since we redeclared the function to be in namespace scope, we don't even have to worry about ADL. Both calls should find ::get<int>(obj<int, float, double> ), which is a friend, and so the code should compile and link. gcc allows the unqualified call but not the qualified call, clang allows neither.

我们可以通过简单地 not 在类内部定义功能模板来完全避开这两个CWG问题:

We could sidestep both CWG issues entirely by simply not defining the function template inside the class:

template<typename ...Args>
class obj {
 bool p = false;

 template<typename T, typename... Args2>
 friend T get(const obj<Args2...> &o);
};

template<typename T, typename... Args>
T get(const obj<Args...> &o) { return o.p; }

使用这种表达方式,两个编译都允许 合格的 get 调用.

With this formulation, both compiles allow both qualified and unqualified invocation of get.

如果我们重写代码,使 obj 是一个类而不是一个类模板,但其他所有条件都相等:

If we rewrite the code such that obj is a class and not a class template, but all else being equal:

class obj {
    bool p = false;

    template <class T>
    friend T get(const obj& o) { return o.p; }
};

template <class T> T get(const obj& );

bool test(const obj& a) {
#ifdef UNQUAL
    return get<int>(a);
#else
    return ::get<int>(a);
#endif
}

两个编译器均允许两种调用.但是,我在规则中没有意识到 obj 是普通类和类模板之间的区别.

Both compilers allow both invocations. But there is no difference that I'm aware of in the rules between obj being a normal class and a class template.

这篇关于模板对象的模板朋友功能和名称空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆