How to limit the scope of `using` outside a function in C ++?

I want to define some static elements of template specialization, for example:

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 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 generates an error message: expected unqualified-id . Are there any ways to limit the using scope in outer space?

+6
source share
2 answers

You cannot embed code blocks (ie {...} ) outside functions.

Let's say we have this common part:

 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 import names into namespace A , making them accessible through the A:: qualification:

 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 suppose this is undesirable, and you see it as pollution. So, the only thing you can do is use an extra namespace to reduce 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 prevents the removal of namespace A from unwanted names, although it is an "implementation namespace" T (which is a terrible name for a namespace !!!).

The third option specializes in 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 opt out of declaring a struct template as follows:

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

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

+5
source

This is a difficult problem. There are several limitations.

  • Template specialization should be done in the namespace scope, this excludes your local bindings for the { } scope.
  • specialization of the template lives in the same namespace as the main template, this eliminates the local namespace detail1 { } and namespace detail2 { } inside namespace A Although g ++ makes this decision incorrectly, Clang rejects it correctly (Note: this was where I got stuck yesterday and temporarily deleted this answer).
  • using directives and declarations pollute the namespace of all clients that include this header Templates
  • living in inline namespaces can be specialized in all of their surrounding namespaces, but declaring namespace A as inline only works if it is enclosed in a sequence of names A and B , which is not possible.

The cleanest approach is to use the libA and libB namespace libA for the long sequences A1::A2::...::AN and B1::B2::...::BN nested namespaces and export these aliases from your headers . This simplifies both client code and actual custom templates.

 #include <iostream> // file Ch namespace A { template<class T> struct C { static int member1_, member2_, member3_; }; } // namespace A // file Ah 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 Bh 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 when repeating MyClass1 and MyClass2 is still a mild symptom of the boiler plate, but the code is much more compact with namespace aliases.

+3
source

Source: https://habr.com/ru/post/970730/


All Articles