简体   繁体   English

哪个是更专业的模板功能? clang和g ++有所不同

[英]Which is the more specialized template function? clang and g++ differ on that

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: 在使用可变参数模板时,遵循这个SO问题 (注意:并不是强制要求遵循这个问题),对于以下模板重载函数,我得到了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...> 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 代码: 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? 最后两个案例(4和5)存在问题:我对更专业模板的期望是错误的吗? and if so, who is right in case 4, clang or g++? 如果是这样,谁是正确的4,clang或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). 试图自己回答这个问题,我多次通过规范中的“更专业”规则(14.5.6.2功能模板的部分排序)cppreference - 似乎更专业的规则应该给出我期待的结果(如果不是,可能会出现歧义错误,但事实并非如此)。 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 . 等等(1):请不要急于带上Herb Sutter的“ 不要超载模板 ”和他的模板方法测验 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. 等(2):请不要急于带来其他可能的解决方案。 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: 正如Holt所说,当涉及到可变参数模板参数推导时,标准是非常严格的:

14.8.2.5/9 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. 如果P具有包含T或i的形式,则将相应模板参数列表P的每个参数Pi与对应的模板参数列表A的对应参数Ai进行比较。 如果P的模板参数列表包含包扩展,则不是最后一个模板参数,整个模板参数列表是非推导的上下文。 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. 如果Pi是包扩展,则将Pi的模式与A的模板参数列表中的每个剩余参数进行比较。每个比较推导出由Pi扩展的模板参数包中的后续位置的模板参数。

This as interpreted by TC would mean that Ts1... can be deduced from the second argument but it leaves no room to Ts2... deduction. 这由TC解释意味着Ts1...可以从第二个参数中推导出来,但它没有留下Ts2...演绎的空间。 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 eg: 因此,显然clang将在这里正确,gcc将是错误的...... 只有当第二个参数包含完全相同的模板参数时才应选择重载,例如:

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. 仍然是示例5.不满足此要求,并且不允许编译器选择过载。

information only: not an answer. 仅供参考:不是答案。 This is a response to a question in the comments: 这是对评论中的问题的回答:

on gcc5.3 making the following small change induces it to produce the expected results, or at least the same results as clang. 在gcc5.3上进行以下小改动会导致它产生预期的结果,或至少与clang相同的结果。

rhodges@dingbat:~$ cat nod.cpp
#include <iostream>

using namespace std;

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

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

template <typename... Ts1, typename... Ts2 >
constexpr bool starts_with(pack<Ts1..., Ts2...>, pack<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;
}


rhodges@dingbat:~$ g++ -std=c++14 nod.cpp && ./a.out
false
false
false
true
false
rhodges@dingbat:~$ g++ --version
g++ (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rhodges@dingbat:~$

and for the record, modifying the program to evaluate all packs in deduced contexts brings success on both platforms: 并且为了记录,修改程序以评估推断的上下文中的所有包在两个平台上都取得了成功:

rhodges@dingbat:~$ cat nod.cpp
#include <iostream>

using namespace std;

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

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

template<typename...LRest>
constexpr bool starts_with_impl(pack<LRest...>, pack<>)
{
    return true;
}

template<typename First, typename...LRest, typename...RRest>
constexpr bool starts_with_impl(pack<First, LRest...>, pack<First, RRest...>)
{
    return starts_with_impl(pack<LRest...>(), pack<RRest...>());
}

template <typename... Ts1, typename... Ts2 >
constexpr bool starts_with(pack<Ts2...> p1, pack<Ts1...> p2) {
    return starts_with_impl(p1, p2);
}

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;
}


rhodges@dingbat:~$ g++ -std=c++14 nod.cpp && ./a.out
false
false
false
true
true

Credit to WF for guiding me in this direction. 感谢WF指导我这个方向。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 具有已删除“一般”情况的专用模板功能无法使用g ++ &lt;= 4.8.0和clang ++进行编译 - Specialized template function with deleted “general” case fails to compile with g++ <=4.8.0 and clang++ 无法使用 std::enable_if 在 g++/clang++ 上编译显式专用模板结构 - Failing to compile explicitly specialized template struct on g++/clang++ with std::enable_if constexpr静态模板函数:g ++错误是对clang的警告 - constexpr static template function: g++ error is a warning on clang 模板元编程 - g ++吃它,clang没有 - template metaprogramming - g++ eats it, clang does not 用于模板实例化的Clang / g ++选项 - Clang/g++ options for template instantiation g ++和clang ++在模板类中定义的朋友模板函数的不同行为 - g++ and clang++ different behaviour with friend template function defined inside a template class Clang“无法推断模板参数”,而gcc / g ++可以。 哪个是对的? - Clang “ couldn't infer template argument ” whereas gcc / g++ can. Which is right? 自动模板参数:g ++ 7.3 vs clang ++ 6.0:哪个编译器正确? - auto template parameters: g++ 7.3 vs clang++ 6.0 : Which compiler is correct? clang / g ++与朋友功能的区别 - clang/g++ difference with friend function G ++,clang ++和std :: function - G++, clang++ and std::function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM