5

I have a forward declaration of a template class in a nested namespace

namespace n1
{
    namespace n2
    {
        template <typename T, typename S>
        struct A;
    }
    using n2::A;
}

followed by a definition, which in fact is in a different file, with stuff in between:

struct X { };

namespace n1
{
    namespace n2
    {
        template <typename T, typename S = X>
        struct A { };
    }
    using n2::A;
}

Then the following is always ok:

n1::n2::A <int> a;

but this shortcut

n1::A <int> a;

gives a compile error in clang

error: too few template arguments for class template 'A'

unless I remove the forward declaration; g++ accepts both.

clang appears to stay with the first declaration which does not include the default template argument (and I cannot include it, because I have not defined X yet).

There is no problem if I use a single namespace (but this is not a solution).

What am I doing wrong, or, which compiler is correct? How can the shortcut work together with the forward declaration, and the nested namespace? I need all of them.

Forward-declaring X + default argument for S of course works, but would be too tedious (there are dozens of them actually, and the whole file structure would change).

iavr
  • 7,547
  • 1
  • 18
  • 53
  • The fact that there's stuff in between and that's in a different file doesn't matter for clang. [Live example](http://coliru.stacked-crooked.com/view?id=fd521dbdf5) – dyp Aug 31 '13 at 00:53
  • And according to [temp.param]/10, this issue is unrelated to templates, as the mechanism is defined in terms of function default arguments. [Live example](http://coliru.stacked-crooked.com/view?id=17873283ec) – dyp Aug 31 '13 at 00:57
  • This might be a bit more complicated than I originally thought. [dcl.fct.default]/9 actually says that your code should work, but there's an [open issue](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1551) with the *using-declaration* where the proposed fix introduces a contradiction to this paragraph and would break your code. – dyp Aug 31 '13 at 01:35
  • I think point 3 in the proposed resolution of this issue answers the question (then, unfortunately clang is right) but also gives an idea: skip the first using-declaration in n1. Possibly make a using-declaration in another namespace to do whatever stuff is needed, and finally a using-declaration in n1 after the definition. Thanks! – iavr Aug 31 '13 at 01:48
  • @iavr see my answer below, GCC also doesn't accept it `src/main.cpp:26:17: error: wrong number of template arguments (1, should be 2)` GCC ftw btw. – Alec Teal Aug 31 '13 at 07:20

2 Answers2

2

template<class T> using myname = some::templatething<T>;

Then you can use myname

In your case stick a template<class T,class S=X> using A = n2::A<T,S>; in your n1

Just wrote a gem of an answer related to this Symbol not found when using template defined in a library there btw, have a read.

Okay it's not been ticked, so I'm gonna help some more!

will not compile

#include <iostream>

namespace n1 {
namespace n2 {
template<class U,class V> struct A;
}
template<class U,class V> using A = n2::A<U,V>;
}

static n1::A<int,int>* test;

struct X {};
namespace n1 {
namespace n2 {
template<class U,class V> struct A {};
}
template<class U,class V=X> using A = n2::A<U,V>;
}

static n1::A<int> test2;


int main(int,char**) {

    return 0;
}

Why? C++'s "first declaration rule"

Here's the compiler's output:

make all 
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -Isrc -c src/main.cpp -o build/main.o
src/main.cpp:26:17: error: wrong number of template arguments (1, should be 2)
 static n1::A<int> test2;
                 ^
src/main.cpp:13:47: error: provided for ‘template<class U, class V> using A = n1::n2::A<U, V>’
 template<class U,class V> using A = n2::A<U,V>;
                                               ^
src/main.cpp:26:24: error: invalid type in declaration before ‘;’ token
 static n1::A<int> test2;
                        ^
src/main.cpp:16:24: warning: ‘test’ defined but not used [-Wunused-variable]
 static n1::A<int,int>* test;
                        ^
src/main.cpp:26:19: warning: ‘test2’ defined but not used [-Wunused-variable]
 static n1::A<int> test2;
                   ^
make: *** [build/main.o] Error 

Okay it's whining about unused variables, fair enough, but notice it's reference to line 13, that's because it's used the first definition default template arguments are really quite primitive and I can't quote specification at you right now. https://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fdefault_args_for_templ_params.htm

that may offer some insight though.

Anyway notice that:

This compiles

#include <iostream>

namespace n1 {
namespace n2 {
template<class U,class V> struct A;
}
template<class U,class V> using A = n2::A<U,V>;
}

static n1::A<int,int>* test;

struct X {};
namespace n1 {
namespace n2 {
template<class U,class V> struct A {};
}
template<class U,class V=X> using B = n2::A<U,V>;
}

static n1::B<int> test2;


int main(int,char**) {

    return 0;
}

Because B has no prior definition.

Build output

make all 
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -Isrc -c src/main.cpp -o build/main.o
src/main.cpp:16:24: warning: ‘test’ defined but not used [-Wunused-variable]
 static n1::A<int,int>* test;
                        ^
src/main.cpp:26:19: warning: ‘test2’ defined but not used [-Wunused-variable]
 static n1::B<int> test2;
                   ^
g++  build/main.o  -o a.out

See, fine :)

Remember with forward declarations you can only use *s and &s (as they are of known size, fixed size infact) - oh and &&s

So you need to get that default in there right away!

Community
  • 1
  • 1
Alec Teal
  • 5,770
  • 3
  • 23
  • 50
  • Thanks, This avoids using the real A in n1 before it is complete, and uses a different *name* for the using-declaration; but I find it too complicated. For the time, I have removed the forward-declaration and declared the 'stuff' after A is complete, which I think is better design. If I really need it in the future, and as I wrote in my comment below the question, I would prefer to move the using-declaration to another namespace rather than rename it, thus avoiding template arguments altogether - I'll add an answer for that. – iavr Aug 31 '13 at 12:49
  • This issue is different from the OP's, as you're using alias templates declarations (`template < /*...*/ > using X = /*...*/;`) whereas the OP is using "simple" (non-template) alias declarations. The effects are different (both clang++ and g++ reject it), and this doesn't have to do with namespaces at all -> [Live example](http://coliru.stacked-crooked.com/view?id=b6f6a922c2). It might have to do with the [open issue 1349](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1349). – dyp Aug 31 '13 at 12:49
1

I have found this as a most convenient workaround:

namespace n1
{
    namespace n2
    {
        template <typename T, typename S>
        struct A;
    }

    namespace fwd { using n2::A; }
}

// stuff on n1::fwd::A;

struct X { };

namespace n1
{
    namespace n2
    {
        template <typename T, typename S = X>
        struct A { };
    }

    using n2::A;
}

That is, move the first using-declaration to another namespace. Now the 'stuff' could go like this:

namespace n1
{
    namespace stuff
    {
        using namespace fwd;

        template <typename T>
        struct R /*...*/;

        template <typename T, typename S>
        struct R <A <T, S> > /*...*/;
    }
}

using A without any namespaces. There is only one n1 in my project, but lots of n2, so moving all forward using-declarations in a single namespace n1::fwd is more convenient, given that I cannot just use n1.

iavr
  • 7,547
  • 1
  • 18
  • 53