模板对象的模板朋友功能和名称空间 [英] template object's template friend functions and namespaces
问题描述
在下面的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
功能模板有两个未解决的问题: 2174 .前者对它完全有效的程度提出质疑,而后者则是关于基于那些功能模板的实际实例化可能发生的odr违规的问题.我不确定哪个编译器是正确的(以前认为这两个都是错误的),但是在这种情况下,标准中可能只是简单地指定了不足或错误的含义.
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屋!