Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward declaration of template class in nested namespace: where should default template arguments go?

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

like image 927
iavr Avatar asked Oct 21 '22 02:10

iavr


1 Answers

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!

like image 93
Alec Teal Avatar answered Oct 27 '22 10:10

Alec Teal