这个问题是关于使用flag / Za的Visual Studio C ++ 2013中模板和静态积分常量之间的关系。它对升级库有影响。
首先,让我们检查没有模板的代码:
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;
}
根据compiler option /Za的手册页:“在标准(/ Za)下,您必须为数据成员制定一个类外定义”。该页面中的示例和上面的代码声明了类中的静态常量,并在那里指定了它的值。在this link中解释了对课外定义的需求。
现在,让我们看一下模板的问题。
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;
}
使用/ Za进行编译时,链接器会抛出错误“LNK2019:未解析的外部符号”。选项/Ze不会出现该错误。主要问题是某些boost库在类似于上述snipet的代码中使用BOOST_STATIC_CONSTANT和BOOST_NO_INCLASS_MEMBER_INITIALIZATION。
黑客攻击:
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;
}
此代码现在使用/ Za编译。
问题:
1)C ++ 11标准对模板和静态积分常数有何看法?可以/必须具有类外定义,但它们的值是否在类定义中提供?
2)boost有一些解决方法吗?
更新
将std::max
保留在代码中非常重要,因为(我认为)它会尝试获取对其参数的引用。如果使用b_<a
,则编译器只会优化这些常量。
答案 0 :(得分:5)
首先,类中静态数据成员的声明永远不是定义。 如果你使用那个变量,必须有一个定义 - 当然是在课外。
std::max
确实使用a
,因为它的参数是引用,如果引用绑定到它们([basic.def.odr] / 3),则变量使用odr。 (这确实是max
的一个问题 - 它确实不应该使用a
。)
在@ sehe的回答中,他直接使用三元运算符,避免了使用odr的问题,因为lvalue-to-rvalue转换会立即应用并产生一个常量表达式。
这很简单。当需要定义类模板的静态数据成员时,即当该成员在您的情况下使用时,将实例化(命名空间作用域)定义。 [temp.inst] / 2:
除非是类模板或成员模板的成员 显式实例化或明确专门化,专业化 当特化是隐式实例化成员的时候 在需要成员定义存在的上下文中引用; 特别是,初始化(以及任何相关的副作用) 除非静态数据成员,否则不会发生静态数据成员 本身的使用方式需要静态的定义 数据成员存在。
定义完全按照你的方式完成。 [temp.static] / 1:
静态数据成员或静态数据成员模板的定义 可以在包含该定义的命名空间范围内提供 静态成员的类模板。
[示例:
template<class T> class X { static T s; }; template<class T> T X<T>::s = 0;
当成员为const
整数类型时,可以在类中的声明中提供初始化程序,但这不会影响ODR在这方面的语义。仍然需要以相同的方式定义该定义,并按照您的方式编写。
因此,您看到的只是一个VC ++错误。
答案 1 :(得分:2)
我使用了很长时间的解决方法,最近在c ++ 11中变得更有用:
<强> Live On Coliru 强>
struct easy
{
enum : int { a = 5 };
int b;
constexpr easy(int b_) : b(b_<a? a : b_)
{}
};
它变得更有用,因为您现在可以指定基础类型:
struct Container
{
enum special_position : size_t { npos = size_t(-1), at_bof = 0 };
};
当然它仅限于(userdefined / primitive)整数类型。
外部定义的常量可能具有以下优点:只需重新编译定义值的转换单元即可实际更改它们。