哪个是更专业的模板功能? clang和g ++不同 [英] Which is the more specialized template function? clang and g++ differ on that

查看:236
本文介绍了哪个是更专业的模板功能? clang和g ++不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用可变参数模板时,这个问题(注意:这不是强制要去那里跟随这个问题),我来了一个不同的行为clang(3.8)和g ++(6.1)为以下模板重载函数:

  template< class ... Ts> 
struct pack {};

template< class a,class b>
constexpr bool starts_with(a,b){
return false;
}

template< template< typename ...> class PACK_A,
template< typename ...>类PACK_B,类型名称... Ts1,类型名称... Ts2>
constexpr bool starts_with(PACK_A return true;
}

int main(){
std :: cout<< std :: boolalpha;
std :: cout<< starts_with(pack< int,float,double>(),
pack< float,int,double>())< std :: endl;
std :: cout<< starts_with(pack< int,float,double>(),
pack< int,float,double,int>())< std :: endl;
std :: cout<< starts_with(pack< int,float,double>(),
pack< int,float,int>())< std :: endl;
std :: cout<< starts_with(pack< int,float,double>(),
pack< int,float,double>())< std :: endl;
std :: cout<< starts_with(pack< int,float,double>(),
pack< int>())< std :: endl;
}

代码: http://coliru.stacked-crooked.com/a/b62fa93ea88fa25b



输出



  | --- | ---------------------------- ------------------------------------------------- 
| #| starts_with(a,b)|预期| clang(3.8)| g ++(6.1)|
| - | ----------------------------------- | ----- -------- | ------------- | ------------- |
| 1 | a:pack< int,float,double>()|假|假|假|
| | b:pack< float,int,double>()| | | |
| --- | ----------------------------------- | ----- -------- | ------------- | --------- --- |
| 2 | a:pack< int,float,double>()|假|假|假|
| | b:pack< int,float,double,int>()| | | |
| --- | ----------------------------------- | ----- -------- | ------------- | --------- --- |
| 3 | a:pack< int,float,double>()|假|假|假|
| | b:pack< int,float,int>()| | | |
| --- | ----------------------------------- | ----- -------- | ------------- | --------- --- |
| 4 | a:pack< int,float,double>()|真|真|假|
| | b:pack< int,float,double>()| | | |
| --- | ----------------------------------- | ----- -------- | ------------- | --------- --- |
| 5 | a:pack< int,float,double>()|真|假|假|
| | b:pack< int>()| | | |
| --- | ----------------------------------------- ------------------------------------ |

最后两种情况(4和5)更专业的模板错了吗?如果是,谁是情况4,clang或g ++是正确的? (注意,代码编译时没有任何错误或警告,但有不同的结果)。



尝试回答自己,我走了几次通过更专业规范中的规则(14.5.6.2部分排序功能模板) cppreference 中 - 似乎更专业的规则给出我期望的结果(如果没有,可能会出现歧义错误,但这不是这种情况)。






等待(1):请不要急于带上不喜欢重载模板Herb Sutter及其模板方法测验。这些都是非常重要的,但语言仍然允许模板重载! (这确实是一个加强点,为什么你不应该重载模板 - 在某些边缘情况下,它可能会混淆两个不同的编译器,或混淆程序员。但问题不是是否使用它是:



等待(2):请不要急于以带来其他可能的解决方案。有肯定。以下是两个:一个包含内部结构另一个具有内部静态方法。这两个都是合适的解决方案,两者都按预期工作,但关于上述模板重载行为的问题仍然存在。

解决方案

在涉及可变参数模板参数推导时,标准是非常严格的:


14.8.2.5/9



如果P具有包含T或i的形式,则将
相应模板参数列表P的每个参数Pi与对应的模板参数列表的相应
参数Ai进行比较A. 如果P的
模板参数列表包含不是
上一个模板参数的包扩展,则整个模板参数列表是一个
未推导的上下文。 strong>如果Pi是包扩展,则将Pi
的模式与模板参数列表
中的每个剩余变元进行比较。每个比较推导出用于随后的
位置的模板变元由Pi扩展的模板参数包。


将意味着 Ts1 ... 可以从第二个参数推导出来,但它没有留给 Ts2 ... 扣除。因为这样显然clang将是正确的在这里和gcc将是错误的...重载应该为此选择只有第二个参数将包含完全相同的模板参数,例如:

  starts_with(pack< int,float,double>(),pack< int,float,double>())



仍然示例5.不满足此要求,不允许编译器选择过载。


While playing with variadic templates, following this SO question (note: it is not mandatory to go there for following this question), I came to a different behavior of clang (3.8) and g++ (6.1) for the following template overloaded functions:

template <class... Ts>
struct pack { };

template <class a, class b>
constexpr bool starts_with(a, b) {
    return false;
}

template <template <typename...> class PACK_A,
          template <typename...> class PACK_B, typename... Ts1, typename... Ts2>
constexpr bool starts_with(PACK_A<Ts1..., Ts2...>, PACK_B<Ts1...>) {
    return true;
}

int main() {
   std::cout << std::boolalpha;
   std::cout << starts_with(pack<int, float, double>(),
                            pack<float, int, double>())        << std::endl;
   std::cout << starts_with(pack<int, float, double>(),
                            pack<int, float, double, int>())   << std::endl;
   std::cout << starts_with(pack<int, float, double>(),
                            pack<int, float, int>())           << std::endl;
   std::cout << starts_with(pack<int, float, double>(),
                            pack<int, float, double>())        << std::endl;
   std::cout << starts_with(pack<int, float, double>(),
                            pack<int>())                       << std::endl;
}

Code: http://coliru.stacked-crooked.com/a/b62fa93ea88fa25b

Output

|---|-----------------------------------------------------------------------------|
| # |starts_with(a, b)                  | expected    | clang (3.8) | g++ (6.1)   |
|---|-----------------------------------|-------------|-------------|-------------|
| 1 |a: pack<int, float, double>()      |  false      |  false      |  false      |
|   |b: pack<float, int, double>()      |             |             |             |
|---|-----------------------------------|-------------|-------------|--------- ---|
| 2 |a: pack<int, float, double>()      |  false      |  false      |  false      |
|   |b: pack<int, float, double, int>() |             |             |             |
|---|-----------------------------------|-------------|-------------|--------- ---|
| 3 |a: pack<int, float, double>()      |  false      |  false      |  false      |
|   |b: pack<int, float, int>()         |             |             |             |
|---|-----------------------------------|-------------|-------------|--------- ---|
| 4 |a: pack<int, float, double>()      |  true       |  true       |  false      |
|   |b: pack<int, float, double>()      |             |             |             |
|---|-----------------------------------|-------------|-------------|--------- ---|
| 5 |a: pack<int, float, double>()      |  true       |  false      |  false      |
|   |b: pack<int>()                     |             |             |             |
|---|-----------------------------------------------------------------------------|

The last two cases (4 and 5) are in question: are my expectation for the more specialized template wrong? and if so, who is right in case 4, clang or g++? (note that the code compiles without any error or warning on both, yet with different results).

Trying to answer that myself, I went several times through the "more specialized" rules in the spec (14.5.6.2 Partial ordering of function templates) and in cppreference -- it seems that the more specialized rule shall give the result I'm expecting (one may expect ambiguity error if not, but this is not the case either). So, what am I missing here?


Wait (1): please don't rush and bring the "prefer not to overload templates" of Herb Sutter and his template methods quiz. These are surely important, but the language still allows templates overloading! (It is indeed a strengthening point why you should prefer not to overload templates -- in some edge cases it may confuse two different compilers, or confuse the programmer. But the question is not whether to use it or not, it is: what is the right behavior if you do use it?).

Wait (2): please don't rush to bring other possible solutions. There are for sure. Here are two: one with inner struct and another with inner static methods. Both are suitable solutions, both work as expected, yet the question regarding the above template overloading behavior still stays.

解决方案

As Holt mentioned the standard is very strict when it comes to variadic template parameters deduction:

14.8.2.5/9

If P has a form that contains T or i, then each argument Pi of the respective template argument list P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi.

This as interpreted by T.C. would mean that Ts1... can be deduced from the second argument but it leaves no room to Ts2... deduction. As such apparently clang would be the right here and gcc would be wrong... The overload should be therefor chosen only if the second parameter would contain exactly the same template parameters e.g:

starts_with(pack<int, float, double>(), pack<int, float, double>())

Still example 5. does not fulfil this requirement and does not allow compiler to chose the overload.

这篇关于哪个是更专业的模板功能? clang和g ++不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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