6

I want to define some static members of template specialization, like this:

namespace A {

template <> int C<A1::A2::...::MyClass1>::member1_ = 5;
template <> int C<A1::A2::...::MyClass1>::member2_ = 5;
template <> int C<A1::A2::...::MyClass1>::member3_ = 5;

template <> int C<B1::B2::...::MyClass2>::member1_ = 6;
template <> int C<B1::B2::...::MyClass2>::member2_ = 6;
template <> int C<B1::B2::...::MyClass2>::member3_ = 6;

...

}

But to simplify the code (and make it look more structured), I want to do something like this:

namespace A {

{
  using T = A1::A2::...::MyClass1;
  template <> int C<T>::member1_ = 5;
  template <> int C<T>::member2_ = 5;
  template <> int C<T>::member3_ = 5;
}

{
  using T = B1::B2::...::MyClass2;
  template <> int C<T>::member1_ = 6;
  template <> int C<T>::member2_ = 6;
  template <> int C<T>::member3_ = 6;
}

...

}

The compiler gives an error: expected unqualified-id. Are there ways to limit the using scope in an "outer" space?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
abyss.7
  • 13,882
  • 11
  • 56
  • 100

2 Answers2

5

You cannot nest code blocks (i.e. {...}) outside functions.

Let's say we have this part in common:

namespace A1 { namespace A2 {
  class MyClass1;
}}

namespace B1 { namespace B2 {
  class MyClass2;
}}

namespace A {
template<typename T>
struct C
{
  static int member1_;
  static int member2_;
  static int member3_;
};
}

You can either import the names into namespace A, making them available through the qualification A:::

namespace A {
using A1::A2::MyClass1;
template <> int C<MyClass1>::member1_ = 5;
template <> int C<MyClass1>::member2_ = 5;
template <> int C<MyClass1>::member3_ = 5;

using B1::B2::MyClass2;
template <> int C<MyClass2>::member1_ = 6;
template <> int C<MyClass2>::member2_ = 6;
template <> int C<MyClass2>::member3_ = 6;
}

but I assume that is unwanted and you see this as pollution. So then the only thing you can do is use an extra namespace to decrease the number of :::

namespace A {
namespace T {
using T1 = A1::A2::MyClass1;
using T2 = B1::B2::MyClass2;
}

template <> int C<T::T1>::member1_ = 5;
template <> int C<T::T1>::member2_ = 5;
template <> int C<T::T1>::member3_ = 5;


template <> int C<T::T2>::member1_ = 6;
template <> int C<T::T2>::member2_ = 6;
template <> int C<T::T2>::member3_ = 6;
}

This keeps your namespace A clear of unwanted typenames, although it introduces an "implementation namespace" T (which is a terrible name for a namespace!!!).

A third alternative specializes struct template C for the types you want:

namespace A{
template<>
struct C<A1::A2::MyClass1>
{
  static const int member1_ = 5;
  static const int member2_ = 5;
  static const int member3_ = 5;
};

template<>
struct C<B1::B2::MyClass2>
{
  static const int member1_ = 5;
  static const int member2_ = 5;
  static const int member3_ = 5;
};
}

Note that this requires static const data members. You can also do away with a declaration of the struct template like so:

namespace A {
template<typename T>
struct C;
}

to limit its use (at compile time) to only the types you want. This would me my preferred solution.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
3

This is a difficult problem to solve. There are several constraints.

  • template specializations have to happen at namespace scope, this rules out your local scope braces { }.
  • a template specialization lives in the same namespace as the primary template, this rules out local namespace detail1 { } and namespace detail2 { } inside namespace A. Although g++ incorrectly accepts this solution, but Clang correctly rejects this (Note: this was where I was stuck earlier today and temporarily deleted this answer).
  • using directives and declarations pollute the namespace of all clients who include this header
  • templates living in inline namespaces can be specialized inside all their enclosing namespace but declaring namespace A to be inline only works if it is enclosed by both the A and B namespace sequences, which is impossible.

The cleanest approach is to use namespace aliases libA and libB for the lengthy A1::A2::...::AN and B1::B2::...::BN sequences of nested namespaces, and to export those aliases from their headers. This simplifies both client code and the actual template specializations.

#include <iostream>

// file C.h

namespace A {

template<class T> 
struct C 
{ 
    static int member1_, member2_, member3_; 
};

}   // namespace A

// file A.h

namespace A1 { namespace A2 {

struct MyClass1 {};

}} // namespace A1, A2

// export namespace alias to hide implementation details
namespace libA = A1::A2;    

namespace A {

template <> int C<libA::MyClass1>::member1_ = 5;
template <> int C<libA::MyClass1>::member2_ = 5;
template <> int C<libA::MyClass1>::member3_ = 5;

}   // namespace A

// file B.h 

namespace B1 { namespace B2 {

struct MyClass2 {};

}} // namespace B1, B2

// export namespace alias to hide implementation details
namespace libB = B1::B2;    

namespace A {

template <> int C<libB::MyClass2>::member1_ = 6;
template <> int C<libB::MyClass2>::member2_ = 6;
template <> int C<libB::MyClass2>::member3_ = 6;

}   // namespace A

// file main.cpp

int main()
{
    std::cout << A::C<libA::MyClass1>::member1_ << "\n";
    std::cout << A::C<libA::MyClass1>::member2_ << "\n";
    std::cout << A::C<libA::MyClass1>::member3_ << "\n";
    std::cout << A::C<libB::MyClass2>::member1_ << "\n";
    std::cout << A::C<libB::MyClass2>::member2_ << "\n";
    std::cout << A::C<libB::MyClass2>::member3_ << "\n";    
}

Live Example.

Note that there is still a mild sympton of boiler plate in the repetition of the MyClass1 and MyClass2, but the code is much more compact with the namespace aliases in place.

Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304