Templates C++;模板静态整数常量:类外定义 这个问题是关于VisualStudioC++ 2013中的模板与静态积分常数之间的关系。它对boost库有影响

Templates C++;模板静态整数常量:类外定义 这个问题是关于VisualStudioC++ 2013中的模板与静态积分常数之间的关系。它对boost库有影响,templates,c++11,boost,initialization,static-members,Templates,C++11,Boost,Initialization,Static Members,首先,让我们检查没有模板的代码: struct easy { static const int a = 5; const int b; easy( int b_ ) : b( std::max( b_, a ) ) {} }; const int easy::a; int main() { easy d_Easy( 0 ); return 0; } 根据手册页面:“在标准(/Za)下,必须为数据成员进行类外定义”。该页面中的示例和上面的代码

首先,让我们检查没有模板的代码:

struct easy
{
    static const int a = 5;
    const int b;

    easy( int b_ ) : b( std::max( b_, a ) )
    {}
};

const int easy::a;

int main()
{
    easy d_Easy( 0 );
    return 0;
}
根据手册页面:“在标准(/Za)下,必须为数据成员进行类外定义”。该页面中的示例和上面的代码声明了类中的静态常量,并在其中指定了它的值。中解释了类外定义的必要性


现在,让我们看看模板的问题

template< class T >
struct problem
{
    static const int a = 5;
    const int b;

    problem( int b_ ) : b( std::max( b_, a ) )
    {}
};

template< class T >
const int problem< T >::a;

int main()
{
    problem< char > d_Bad( 666 );
    return 0;
}
模板
结构问题
{
静态常数INTA=5;
常数int b;
问题(intb_uub):b(std::max(b_uua))
{}
};
模板
常数整数问题::a;
int main()
{
问题d_Bad(666);
返回0;
}
使用/Za编译时,链接器抛出错误“LNK2019:未解析的外部符号”。选项/Ze中不会出现该错误。主要问题是某些boost库在类似于上述snipet的代码中使用boost_STATIC_常量和boost_NO_INCLASS_MEMBER_初始化


黑客攻击:

template< class T >
struct fixed
{
    static const int a;
    const int b;

    fixed( int b_ ) : b( std::max( b_, a ) )
    {}
};

template< class T >
const int fixed< T >::a = 5;

int main()
{
    fixed< char > d_Good( 777 );
    return 0;
}
模板
结构固定
{
静态常数INTA;
常数int b;
固定(整数b_uub):b(标准::最大值(b_uua))
{}
};
模板
固定常数:a=5;
int main()
{
固定d_良好(777);
返回0;
}
此代码现在使用/Za进行编译

问题:

1) 关于模板和静态积分常数,C++11标准怎么说?它们是否可以/必须具有类外定义,但其值必须在类定义中提供

2) boost是否有一些变通方法


更新


在代码中保留
std::max
是很重要的,因为(我认为)它试图获取对其参数的引用。如果使用
b_p>一个我使用了很长时间并且最近在c++11中变得更有用的变通方法:

struct easy
{
    enum : int { a = 5 };
    int b;

    constexpr easy(int b_) : b(b_<a? a : b_)
    {}
};
当然,它仅限于(用户定义/原语)整数类型


外部定义的常量的好处是,它们实际上可以通过重新编译定义值的转换单元来更改


首先,类中静态数据成员的声明从来不是定义如果您使用该变量,则必须提供定义-当然是课外定义

std::max
odr确实使用了
a
,因为它的参数是引用,而变量是odr,如果引用绑定到它们([basic.def.odr]/3)。(这确实是
max
的问题-它不应该使用
a
,真的。)
在@sehe的回答中,他直接使用了三元运算符,避免了odr的使用,因为左值到右值的转换会立即应用,并产生一个常量表达式

  • 这很简单。当需要类模板的静态数据成员的定义时,即当该成员在您的案例中使用odr时,将实例化(命名空间范围)定义。[临时安装]/2:

    除非类模板或成员模板的成员已 显式实例化或显式专门化,专门化 当指定专门化时,隐式实例化成员的 在要求成员定义存在的上下文中引用; 特别是初始化(以及任何相关的副作用) 除非静态数据成员 本身的使用方式需要定义静态 数据成员必须存在

    这个定义和你做的完全一样。[温度静态]/1:

    静态数据成员或静态数据成员模板的定义 可以在包含 静态成员的类模板

    [示例:

    模板类X{
    静态T-s;
    };
    模板tx::s=0;
    
    当成员是
    const
    integral类型时,可以在类中的声明中提供初始值设定项,但这并不影响ODR在这方面的语义。定义仍然需要以相同的方式提供,编写方式与您所做的相同


  • 因此,您看到的似乎只是一个VC++错误。

    如果要指定基础类型,您不需要
    枚举类吗?@vsoftco否(至少在我的编译器上不需要)@vsoftco:
    部分使其成为强类型。没有它,声明会溢出到容器结构中。
    警告:作用域枚举仅在-std=c++11或-std=gnu++11
    中可用,如果使用
    g++-pedantic
    编译,我认为它应该是无关的:vs.vs.vs.vs.vs.vs.vs在生成的代码中,数据段中存在/不存在
    easy::a
    。"如果有人使用b_,也许我的答案后面的一段对你真正的问题很重要?如果是这样,显然你总是需要将静态成员初始化分离到一个单独的转换单元中。@sehe:这是相关的,因为我无法在Visual Studio C++13中用/Za编译boost::pool。我将罪魁祸首追溯到问题中的行为太长了,读不下去了,我会告诉谁是负责人,我会向Boost人或FELAS女士报告bug。我已经发布了一个几乎详尽的TL比较。DI是正确的:没关系。编译器在代码中引用了<代码>:STD::MAX//CARD>变体(同样应该)。@谢:谢谢。你所做的证明了该页面中的编译器没有显示错误。但是,这些编译器没有问题:#include struct A{static const int A=5;A(){std::cout“std::max确实odr使用了A,因为它的参数是引用”你确定这足以需要定义吗?啊,似乎不是
    struct Container
    {
        enum special_position : size_t { npos = size_t(-1), at_bof = 0 };
    };
    
    template<class T> class X {
        static T s;
    };
    template<class T> T X<T>::s = 0;