首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >哪个是更专业的模板函数?clang和g++在这一点上有所不同

哪个是更专业的模板函数?clang和g++在这一点上有所不同
EN

Stack Overflow用户
提问于 2016-06-20 22:14:14
回答 2查看 439关注 0票数 19

在使用可变模板时,遵循clang (注意:为了回答这个问题,不一定要去那里),对于以下模板重载函数,我发现了this SO question (3.8)和g++ (6.1)的不同行为:

代码语言:javascript
复制
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;
}

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

输出

代码语言:javascript
复制
|---|-----------------------------------------------------------------------------|
| # |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>()                     |             |             |             |
|---|-----------------------------------------------------------------------------|

最后两种情况(4和5)是有问题的:我对更专业的模板的期望是错误的吗?如果是这样的话,在案例4中,谁是对的,clang还是g++?(请注意,代码编译时没有任何错误或警告,但结果不同)。

为了回答这个问题,我多次查看了规范(14.5.6.2 Partial ordering of function templates)cppreference中的“更专门化”规则--似乎更专门化的规则应该会给出我所期望的结果(如果不是,可能会出现歧义错误,但事实也不是这样)。那么,我在这里错过了什么?

等待(1):请不要着急,带上Herb Sutter和他的template methods quiz的"prefer not to overload templates“。这些当然很重要,但是该语言仍然允许模板重载!(这确实是一个加强点,为什么您不应该重载模板--在某些边缘情况下,它可能会混淆两个不同的编译器,或者混淆程序员。但问题不在于是否使用它,而是:如果你使用它,正确的行为是什么?)。

等待(2):请不要着急带来其他可能的解决方案。当然有。这里有两个:one with inner structanother with inner static methods。这两个都是合适的解决方案,都像预期的那样工作,但是关于上面的模板重载行为的问题仍然存在。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-20 23:14:36

正如Holt提到的,当涉及到可变模板参数推导时,标准是非常严格的:

14.8.2.5/9

如果P具有包含T或i的形式,则将相应模板实参列表P的每个实参Pi与A的相应模板实参列表的对应实参Ai进行比较。如果P的模板实参列表包含不是最后一个模板实参的包展开,则整个模板实参列表是非推导上下文。如果Pi是包展开,则将Pi的模式与A的模板实参列表中的每个剩余实参进行比较。每次比较都推导出模板参数包中由Pi展开的后续位置的模板实参。

很明显,当当在这里是对的,而gcc是错的。

代码语言:javascript
复制
starts_with(pack<int, float, double>(), pack<int, float, double>())

仍然是示例5。不满足此要求,并且不允许编译器选择重载。

票数 4
EN

Stack Overflow用户

发布于 2016-06-20 22:45:33

仅供参考:不是答案。这是对评论中的一个问题的回应:

在gcc5.3上,进行以下小的更改会导致它产生预期的结果,或者至少产生与clang相同的结果。

代码语言:javascript
复制
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:~$

根据记录,修改程序以在推导的上下文中评估所有包将在两个平台上都取得成功:

代码语言:javascript
复制
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

感谢W.F.指引我朝这个方向前进。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37924796

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档