18

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?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
user1095108
  • 14,119
  • 9
  • 58
  • 116
  • 4
    +1 good question. my initial reaction was that g++'s acceptance had to be wrong, but then on second thought, it had to be right. hm. – Cheers and hth. - Alf Apr 24 '14 at 08:08
  • 7
    Clang is wrong at least in *wordings* : `previous definition is here`. Well, that is **not** a *definition*; that is a *declaration*. – Nawaz Apr 24 '14 at 08:11
  • 3
    +1. Smart question, and it attempts to avoid typing the type again. Later, if one wants to change the type, just the declaration needs change; it will be reflected automatically in the definition also. – Nawaz Apr 24 '14 at 08:14
  • 2
    VC++ also gives no error. – iDebD_gh Apr 24 '14 at 08:59

3 Answers3

3

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, e.g., 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.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
3

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).

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
2

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.

Community
  • 1
  • 1
Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Good circumstantial evidence, let's wait and see what the language lawyers might say. Care to post a `clang` bug maybe? – user1095108 Apr 24 '14 at 12:16
  • This topic is already on the list, so why there should be another bug report. Especially for this point: It is no bug for clang, it seems to be a open question in the standard so implementers have no chance to do the job in a correct way. – Klaus Apr 24 '14 at 12:18
  • The purported goal of `clang` is to accept everything `gcc` accepts. Therefore this compilation failure could qualify as a bug. Note also, that even you don't know if my question fits the unresolved `clang` issue exactly. – user1095108 Apr 24 '14 at 12:32
  • Feel free to do it. I have not to decide which bug reports should be written :-) For me the clang team know that this topic is still open. – Klaus Apr 24 '14 at 12:43
  • I don't see how the DR or the other question relate to this question. Regardless of whether the static member is a template unto itself or when it is instantiated, the definition declaration matches the declaration and the types do not differ. – Potatoswatter Apr 24 '14 at 13:46
  • @Potatoswatter I agree, and I think it is DR `#2` rather than `#205`. – Johannes Schaub - litb Apr 24 '14 at 17:00
  • @JohannesSchaub-litb Little help with my answer? Or are you writing your own? – Potatoswatter Apr 24 '14 at 17:17
  • @Potatoswatter sorry, I'm already finished :/ You may want to complete your fine answer with a link to DR#2 though :) – Johannes Schaub - litb Apr 24 '14 at 17:18
  • @JohannesSchaub-litb Now I see, thanks! It's bedtime for me though. :P – Potatoswatter Apr 24 '14 at 17:20