首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将模板与Clang中的函数局部类型一起使用时,类型计数器出现意外结果

将模板与Clang中的函数局部类型一起使用时,类型计数器出现意外结果
EN

Stack Overflow用户
提问于 2015-07-04 13:09:48
回答 2查看 221关注 0票数 12

我基于两个类型编写了一个类模板,并根据其模板参数分配了一个唯一的索引:

代码语言:javascript
运行
复制
template<typename SK,typename T>
struct Component {
    static uint const index;
};

期望是,对于每个新类型,index都会递增:

代码语言:javascript
运行
复制
Component<X,A>::index; // 0
Component<X,B>::index; // 1

Component<Y,A>::index; // 0
Component<Y,B>::index; // 1
// ...etc

分配索引的完整代码如下:

代码语言:javascript
运行
复制
using uint = unsigned int;

template<typename SK,typename T>
struct Component
{
    static uint const index;
};

template<typename SK>
class ComponentCount
{
    template<typename CSK,typename CT>
    friend struct Component;

private:
    template<typename T>
    static uint next() {
        return ComponentCount<SK>::get_counter();
    }

    static uint get_counter()
    {
        static uint counter = 0;
        return counter++;
    }
};

这在GCC (5.1)和MSVC中按照预期工作,并进行了以下测试:

代码语言:javascript
运行
复制
// global scope
struct X {};
struct Y {};

int main()
{
    // function scope
    struct Z{};

    uint x0 = Component<X,int>::index;
    uint x1 = Component<X,double>::index;
    uint x2 = Component<X,double>::index;
    uint x3 = Component<X,std::string>::index;
    uint x4 = Component<X,int>::index;
    uint x5 = Component<X,int>::index;

    std::cout << x0 << ", " << x1 << ", " << x2 << ", "
              << x3 << ", " << x4 << ", " << x5 << std::endl;

    uint y0 = Component<Y,int>::index;
    uint y1 = Component<Y,double>::index;
    uint y2 = Component<Y,double>::index;
    uint y3 = Component<Y,std::string>::index;
    uint y4 = Component<Y,int>::index;
    uint y5 = Component<Y,int>::index;

    std::cout << y0 << ", " << y1 << ", " << y2 << ", "
              << y3 << ", " << y4 << ", " << y5 << std::endl;

    uint z0 = Component<Z,int>::index;
    uint z1 = Component<Z,double>::index;
    uint z2 = Component<Z,double>::index;
    uint z3 = Component<Z,std::string>::index;
    uint z4 = Component<Z,int>::index;
    uint z5 = Component<Z,int>::index;

    std::cout << z0 << ", " << z1 << ", " << z2 << ", "
              << z3 << ", " << z4 << ", " << z5 << std::endl;

    return 0;
}

输出为

代码语言:javascript
运行
复制
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0

然而,对于Clang (3.6.1),输出有所不同:

代码语言:javascript
运行
复制
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
5, 2, 2, 3, 5, 5

具体而言,为函数本地类型生成的索引(即,'Z')做一些奇怪的事情。这就像它们在每次调用Component<Z,...>时递增和重新分配索引一样。

这一切为什么要发生?这是一个编译器错误吗?在模板中使用函数局部类型时(在C++11之后)有什么特殊的注意事项吗?

一个完整的例子可以在这里找到:http://coliru.stacked-crooked.com/a/7fcb989ae6eab476

==编辑==

我决定把这个问题发布到clang的bugtracker上,所以如果其他人遇到这个问题:

https://llvm.org/bugs/show_bug.cgi?id=24048

EN

回答 2

Stack Overflow用户

发布于 2015-07-04 14:34:46

在我看来这是个bug。我不知道应该对函数局部类型和全局类型有影响的C++11规则。

如果您使用dump the assembler,您可以注意到,对于XY,这些值是实际计算的,而对于Z,这些值是预先计算的。根本不生成用于计数器静态变量初始化的保护变量。

代码语言:javascript
运行
复制
.Ltmp87:
    #DEBUG_VALUE: main:z5 <- 5
    #DEBUG_VALUE: main:z4 <- 5
    #DEBUG_VALUE: main:z3 <- 3
    #DEBUG_VALUE: main:z2 <- 2
    #DEBUG_VALUE: main:z1 <- 2
    #DEBUG_VALUE: main:z0 <- 5
    .loc    6 54 5                  # main.cpp:54:5
    movl    std::cout, %edi
    movl    $5, %esi
    .loc    6 74 5                  # main.cpp:74:5
    callq   std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
票数 1
EN

Stack Overflow用户

发布于 2015-07-04 20:17:20

我不知道这是否是clang中的bug,但似乎您当前版本的Component::template next有问题。

为了说明这一点,我将函数成员的访问权限从"private“更改为"public”。然后使用以下代码:

代码语言:javascript
运行
复制
for(int i=0; i<5;++i)
    std::cout<< ComponentCount<int>::next<int>() <<" ";

我得到了:

0 1 2 3 4

因此,也许您应该考虑将您的next实现更改为如下所示:

代码语言:javascript
运行
复制
template<typename SK>
class ComponentCount
{
    template<typename CSK,typename CT>
    friend struct Component;

private:
    template<typename T>
    static uint next() {
        static uint index = get_counter();
        return index;
    }

    static uint get_counter()
    {
        static uint counter = 0;
        return counter++;
    }
};

这样我就得到了关于gcc,clang3.6和VS2015的预期结果

g++ (GCC) 5.1.0版权所有(C) 2015自由软件基金会,公司。这是自由软件;请参阅复制条件的源代码。没有任何保证;即使是适销性或对特定目的的适用性也没有。

0,1,1,2,0,0

0,1,1,2,0,0

0,1,1,2,0,0

clang版本3.6.0 (tags/RELEASE_360/final 235480)目标:x86_64-未知-linux-gnu线程型号: posix

0,1,1,2,0,0

0,1,1,2,0,0

0,1,1,2,0,0

final code + runs of gcc & clang on coliru

编辑打字错误

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

https://stackoverflow.com/questions/31217135

复制
相关文章

相似问题

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