17

For example:

class example{
    public:
        template <class T> static constexpr T var = T(1.5);
};

int main(){

    int a = example::var<int>;

    example obj;
    int b = obj.var<int>;

    return 0;
}

GCC produces error for both: 'example::var<T>' is not a function template and 'var' is not a member template function

Clang compiles correctly the first one but produces an error for the second: cannot refer to member 'var' in 'example' with '.'

According to the C++14 Standard (ISO/IEC 14882:2014):

Section 14, Paragraph 1.

A variable template at class scope is a static data member template.

Section 9.4, Paragraph 2.

A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred to using the class member access syntax, in which case the object expression is evaluated.

Therefore, IMHO, a Variable Template at Class scope (i.e., a Static Data Member Template) could be referred to in both ways. Could it be a bug in the compilers?

The only thing I found to try to justify this behaviour is this sentence in Section 9.4.2, Paragraph 1:

A static data member is not part of the subobjects of a class.

However, the two aforementioned paragraphs are still valid. Besides, I tried the same example referring to other static members such as a variable, a function and a function template, and all of them compile successfully in both GCC and Clang.

class example{
    public:
        static int constexpr variable = 1;
        void static function(){ return; }
        template <class T> void static function_template(){ return; }
};

int main(){

    example obj;

    int a = obj.variable;
    int b = example::variable;

    obj.function();
    example::function();

    obj.function_template<int>();
    example::function_template<int>();

   return 0;
}

Thanks in Advance.

Note 1: compiler versions are clang 3.7.0 and gcc 5.2.1.

Note 2: the keyword static is required: Variable template at class scope

Note 3: since I want to initialize the variable template, the keyword constexpr is also required because in my actual code I will instantiate it with float, double and long double (see C++14 Standard (ISO/IEC 14882:2014), Section 9.4.2, Paragraph 3).

Note 4: actual "definitions" of these static data members outside the class (i.e., template <class T> constexpr T example::var;) are not needed in this example cases. I also tried though, but it makes no difference.

4

1 回答 1

2

我将您的第一个代码复制到 Visual Studio 2015 中(编译成功)。我通过添加了一些输出std::cout,我发现 usingb给出了编译器错误uninitialized local variable 'b' useda另一方面,在b不使用时被成功打印。因此,正如您所说,c++ 似乎对访问模板静态成员有点挑剔,要求您通过其完整的限定名称来引用它。

也许更奇怪的是以下几行:

std::cout << example::var<int> << "a\n";

上面的行按预期工作,输出1.5截断为1'a'一个新行。没什么好写的。

std::cout << obj.var<int> << "b\n";

现在这里是有趣的地方......不仅上面的行没有打印出 的值obj.var<int>,而且'b'\n也永远不会被打印出来。我什至针对std::cout'good() fail()bad()函数进行了测试,没有一个报告有任何错误(并且std::cout成功执行了进一步的使用输出)。

我发现的另一个奇怪之处是它auto x = obj.var是合法的,并且发现 x 是 type example。现在,使用全局模板变量执行此操作会导致编译器错误(正如我所期望的第一个错误):

template<typename T> constexpr T ex = 1.5;
auto x = ex // compiler error: argument list for variable template "ex" is missing

此外,我发现var通过另一个模板静态函数的访问是成功的,进一步暗示成员选择在这种情况下不起作用

class example
{
public:
    template <class T> static constexpr T var = T(1.5);
    template <typename T> static void thing()
    {
        std::cout << var<T> << '\n';          // works
        std::cout << example::var<T> << '\n'; // also works
    }
};

现在,就标准而言,我倾向于相信他们的措辞有点……迂腐。您从标准中引用的部分:

不必使用类成员访问语法 (5.2.5) 来引用静态成员。

类范围的变量模板是静态数据成员模板。

似乎暗示这会起作用。我相信这些引用在这种情况下不适用的一点是模板(任何东西)在编译单元中实例化之前并不真正“存在”(即为什么模板的代码经常包含在头文件本身)。

因此,尽管模板变量可以是类的成员,但它的实例不是......由于某种原因......因此需要范围解析运算符而不是成员选择运算符。

但是 IMO,通过成员选择运算符访问静态数据是不好的做法,因为静态数据和函数实际上并不是给定对象的一部分。以与非静态数据相同的方式访问静态数据可能会导致看起来相对无害的代码实际上是有缺陷的逻辑。例如,如果由于某种原因您有一个名为 的非常量静态成员something,您可以编写example_object.something = 42,而不期望在整个程序中对该类的所有其他实例进行任何更改(实际上与全局变量相同的问题)。正因为如此(而且成员访问语法显然不适用于模板静态成员变量这一事实),我建议始终使用范围解析从类外部访问/修改静态内容。example_class::something = 42更清楚地表明我们正在something改变的所有实例example_class。事实上,一些更现代的语言(如 C#)要求您通过类名访问静态数据,除非您在该类中。

鉴于几个编译器在这个小示例程序的不同部分出错,我敢打赌它在标准中没有很好地涵盖(并且在实践中可能不经常使用),并且编译器只是以不同的方式处理它(另一个避免它的理由)。

tl;博士

显然,虽然成员选择语法适用于静态成员变量,但它不适用于模板静态成员变量(尽管编译器似乎没有抱怨)。但是,范围解析语法确实有效,无论如何都应该首选 IMO。

于 2017-01-04T00:48:20.683 回答