C++ 模板静态整数常量:超出 class 定义

C++ template static integer constants: out of class definition

这个问题是关于 Visual Studio C++ 2013 中带有标志 /Za 的模板和静态整数常量之间的关系。它对 boost 库有影响。


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

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: "Under the standard (/Za), you must make an out-of-class definition for data members". The example in that page and the code above declares the static constant within the class and specifies its value there. The need for the out of class definition is explained in 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: unresolved external symbol"。选项 /Ze.The 不会出现该错误,主要问题是一些 boost 库在类似于上述代码段的代码中使用 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 标准对模板和静态整型常量有何规定? Can/must 它们的定义不在 class 范围内,但它们的值在 class 定义中提供?

2) boost 有一些解决方法吗?


更新

在代码中保留 std::max 很重要,因为(我认为)它试图获取对其参数的引用。如果使用 b_<a,那么编译器会简单地优化这些常量。

我用了很长时间并且最近在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)个整数类型。


Externally defined constants may have the benefit that they could actually be changed by only recompiling the translation unit that defines the value.

首先,class 中静态数据成员的声明永远不是定义。 如果您使用该变量,则必须存在定义 - 当然,class。

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

  1. 很简单。当需要 class 模板的静态数据成员的定义时,即当该成员像您的情况一样被 odr 使用时,(命名空间范围)定义被实例化。 [temp.inst]/2:

    Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

    并且定义完全按照您的方式完成。 [temp.static]/1:

    A definition for a static data member or static data member template may be provided in a namespace scope enclosing the definition of the static member’s class template.

    [ Example:

    template<class T> class X {
        static T s;
    };
    template<class T> T X<T>::s = 0;
    

    当成员是 const 整数类型时,可以在声明 in-class 中提供初始值设定项,但这不会影响 ODR 在这方面的语义。仍然需要以相同的方式定义并像您所做的那样编写。

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