title: using vs. typedef - is there a subtle, lesser known difference?
body: <h2>Background</h2>
  
  <p>Everybody agrees that</p>
  
  <pre><code>using &lt;typedef-name&gt; = &lt;type&gt;;
  </code></pre>
  
  <p>is equivalent to</p>
  
  <pre><code>typedef &lt;type&gt; &lt;typedef-name&gt;;
  </code></pre>
  
  <p>and that the former is to be preferred to the latter for various reasons (see Scott Meyers, Effective Modern C++ and various related questions on stackoverflow).</p>
  
  <p>This is backed by [dcl.typedef]:</p>
  
  <blockquote>
    <p>A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword
    becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that
    typedef-name. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.</p>
  </blockquote>
  
  <p>However, consider a declaration such as</p>
  
  <pre><code>typedef struct {
      int val;
  } A;
  </code></pre>
  
  <p>For this case, [dcl.typedef] specifies:</p>
  
  <blockquote>
    <p>If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the
    declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage
    purposes only (3.5).</p>
  </blockquote>
  
  <p>The referenced section 3.5 [basic.link] says</p>
  
  <blockquote>
    <p>A name having namespace scope that has not
    been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
    [...]
    an unnamed class defined in a typedef declaration in which the class has
    the typedef name for linkage purposes [...]</p>
  </blockquote>
  
  <p>Assuming the typedef declaration above is done in the global namespace, the struct <code>A</code> would have external linkage, since the global namespace has external linkage.</p>
  
  <h2>Question</h2>
  
  <p>The question is now whether the same is true, if the typedef declaration is replaced by an alias declaration according to the common notion that they are equivalent:</p>
  
  <pre><code>using A = struct {
      int val;
  };
  </code></pre>
  
  <p>In particular, does the type <code>A</code> declared via the alias declaration ("using") have the same linkage as the one declared via the typedef declaration?</p>
  
  <p>Note that [decl.typedef] does not say that an alias declaration <em>is a</em> typedef declaration (it only says that both introduce a typedef-name) and that [decl.typedef] speaks only of a <em>typedef declaration</em> (not an alias declaration) having the property of introducing a <em>typedef name for linkage purposes</em>.
  If the alias declaration is not capable of introducing a typedef name for linkage purposes, <code>A</code> would just be an alias for an anonymous type and have no linkage at all.</p>
  
  <p>IMO, that's at least one possible, albeit strict, interpretation of the standard. Of course, I may be overlooking something.</p>
  
  <p>This raises the subsequent questions:</p>
  
  <ul>
  <li>If there is indeed this subtle difference, is it by intention or is
  it an oversight in the standard?</li>
  <li>What is the expected behavior of compilers/linkers? </li>
  </ul>
  
  <h2>Research</h2>
  
  <p>The following minimal program consisting of three files (we need at least two separate compilation units) is used to investigate the issue.</p>
  
  <h3>a.hpp</h3>
  
  <pre><code>#ifndef A_HPP
  #define A_HPP
  
  #include &lt;iosfwd&gt;
  
  #if USING_VS_TYPEDEF
  using A = struct {
       int val;
  };
  #else
  typedef struct {
       int val;
  } A;
  #endif
  
  void print(std::ostream&amp; os, A const&amp; a);
  
  #endif // A_HPP
  </code></pre>
  
  <h3>a.cpp</h3>
  
  <pre><code>#include "a.hpp"
  #include &lt;iostream&gt;
  
  void print(std::ostream&amp; os, A const&amp; a)
  {
     os &lt;&lt; a.val &lt;&lt; "\n";
  }
  </code></pre>
  
  <h3>main.cpp</h3>
  
  <pre><code>#include "a.hpp"
  #include &lt;iostream&gt;
  
  int main()
  {
      A a;
      a.val = 42;
      print(std::cout, a);
  }
  </code></pre>
  
  <h2>GCC</h2>
  
  <p>Compiling this with gcc 7.2 with the "typedef" variant compiles cleanly and provides the expected output:</p>
  
  <pre><code>&gt; g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
  &gt; ./a.out
  42
  </code></pre>
  
  <p>The compilation with the "using" variant produces a compile error:</p>
  
  <pre><code>&gt; g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
  a.cpp:4:6: warning: ‘void print(std::ostream&amp;, const A&amp;)’ defined but not used [-Wunused-function]
  void print(std::ostream&amp; os, A const&amp; a)
       ^~~~~
  In file included from main.cpp:1:0:
  a.hpp:16:6: error: ‘void print(std::ostream&amp;, const A&amp;)’, declared using unnamed type, is used but never defined [-fpermissive]
  void print(std::ostream&amp; os, A const&amp; a);
       ^~~~~
  a.hpp:9:2: note: ‘using A = struct&lt;unnamed&gt;’ does not refer to the unqualified type, so it is not used for linkage
  };
   ^
  a.hpp:16:6: error: ‘void print(std::ostream&amp;, const A&amp;)’ used but never defined
  void print(std::ostream&amp; os, A const&amp; a);
       ^~~~~
  </code></pre>
  
  <p>This looks like GCC follows the strict interpretation of the standard above and makes a difference concerning linkage between the typedef and the alias declaration.</p>
  
  <h2>Clang</h2>
  
  <p>Using clang 6, both variants compile and run cleanly without any warnings:</p>
  
  <pre><code>&gt; clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
  &gt; ./a.out
  42
  
  &gt; clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
  &gt; ./a.out
  42
  </code></pre>
  
  <p>One could therefore also ask</p>
  
  <ul>
  <li>Which compiler is correct?</li>
  </ul>