简体   繁体   中英

static member of class template error

I have a problem with this code snippet:

template <typename T>
struct S
{
  static int a;
};

template <typename T>
decltype(S<T>::a) S<T>::a;

clang-3.4 says:

s.cpp:8:25: error: redefinition of 'a' with a different type: 'decltype(S<T>::a)' vs 'int'
decltype(S<T>::a) S<T>::a;
                        ^
s.cpp:4:14: note: previous definition is here
  static int a;
             ^
1 error generated.

But gcc-4.8.2 accepts. Which of the compilers is right? Should I avoid such code in the future?

Clang is demanding that the definition match the declaration at template definition time, whereas GCC and others defer matching until instantiation time (which never even happens for your example).

Clang accepts this:

#include <type_traits>

template <typename T>
struct S
{
  static int a;
};

template <typename T>
typename std::enable_if< true, int >::type S<T>::a; // Resolves before instantiation

but rejects this small change:

template <typename T>
typename std::enable_if< std::is_same< T, T >::value, int >::type S<T>::a;

I cannot recall where the standard dictates when object declaration matching occurs, but I suspect that Clang is within its rights to reject the code. The intent of the standard, if I recall correctly, is that each declaration matches exactly one definition, and that mapping may be determined before instantiation time.

With the looser rule that GCC is apparently applying, you could have two member declarations and two definitions, but each definition may finalize either of the declarations depending on the template parameters.

The code which GCC and MSVC are accepting is ill-formed, no diagnosis required… pending finding the actual standardese buried somewhere in §3 [basic], §7 [dcl.dcl], §8 [dcl.decl], §14 [temp], or maybe somewhere else.


I still cannot find what rule matches object definitions to preceding declarations, but §14.4/2 dictates that decltype(…) cannot be the equivalent (I assume in the declarative sense) to int .

If an expression e involves a template parameter, decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (14.5.6.1). [ Note: however, it may be aliased, eg, by a typedef-name. end note ]

I'm pretty sure that equivalence, not mere aliasing, is necessary for the definition to match the declaration. §14.5.6.1 delves pretty deep into this territory, except it is specifically discussing function signatures.

I think Clang might be right in rejecting this. 14.2p2 says about decltype(e)

If an expression e involves a template parameter, decltype(e) denotes a unique dependent type.

In DR #2 , the discussion trace says

My opinion (which I think matches several posted on the reflector recently) is that the out-of-class definition must match the declaration in the template.

...

In general, if you can match the declarations up using only information from the template, then the declaration is valid.

I think it still matches if one of them uses a typedef (as demonstrated in the DR), because S<T>::type is a member of the current instantiation and the type aliased can be looked up directly. But a decltype(e) , as specified above, will always denote a unique type (during template parse time) except with respect to another decltype(e) that specifies an equivalent expression.


Why did I say might ? Because of 14.6p8

No diagnostic shall be issued for a template for which a valid specialization can be generated.

One could read this as saying the type equivalence check is simply delayed till after instantiation. This, however, would contradict the discussion trace in the DR I think, because they say "if you can match the declarations up using only information from the template, then the declaration is valid" (and I assume the author of this statement meant to be exhaustive about the situations when the declaration is valid).

For me clang is broken here.

All combinations with decltype will fail. Without decltype it works.

template <typename T>
struct S
{   
      static int a;

      using type = decltype( a );
      typedef decltype( a ) type2;
};  

template <typename T>
1) decltype(S<T>::a) S<T>::a;
2) int S<T>::a;
3) typename S<T>::type S<T>::a;
4) typename S<T>::type2 S<T>::a;

1 gcc works, clang fails

2 gcc + clang works

3 gcc works, clang fails

4 gcc works, clang fails

I did some more tries to work around the problem, but could not have any success.

There are some more discussions on that kind of problems: C++ Static member initalization (template fun inside)

Edit: I found that this topic is simply "not resolved" in the standard until now and clang did not implemented it: Take a look at: http://clang.llvm.org/cxx_dr_status.html ( point 205 )

I hope that I did not misunderstood the page. Feel free to correct my interpretation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM