Scaling and default arguments in template declarations in C ++: refining the standard

I read the C ++ 14 section in templates, trying to improve my understanding of the subject and came across this particular rule:

ยง 14.1

12 The template parameter should not be assigned the default arguments by two different declarations in the same area.

[Example:

template<class T = int> class X; template<class T = int> class X { /โˆ—... โˆ—/ }; // error 

- end of example]

In my (relatively uninformed) reading, the specification of "the same area" implies the ability to declare templates in different areas where they are defined.

According to this article about Dr. Dobbs

C ++ identifies five kinds of scope: function, prototype functions, local, namespace and class.

From them I understand that:

  • and (I assume the function prototype, since it extends past functions only to declarations) scope cannot contain template declarations
  • the local area falls within the scope of the function and, therefore, has the same limitations as above
  • you cannot (re) declare any member of a class outside the declaration of that class.

Thus, the potential odd case of resolving declarations outside a specific area (potentially with changed default arguments depending on the scope!) Seems to fall directly onto the shoulders of the namespace area. I experimented a bit:

[ Coliru ]


Command:

 g++ -std=c++1z -O2 -Wall -pedantic -pthread main.cpp && ./a.out 

Code:

 #include <iostream> namespace math{ template <int I, int J> struct Plus{ constexpr static int Value_ = I + J; }; template <int I, int J = 5> struct Minus; // template declaration. } // global-scope template declaration? //template <int I, int J> //struct math::Minus; // error: does not name a type (if no declaration @ line 9) // error: invalid use of math::Minus w/o argument list // (if declaration @ line 9) namespace math{ template <int I, int J> struct Minus{ static int value(); }; namespace{ // anonymous namespace //template <int I, int J = 5> //struct Minus; compiles, but is a declaration of another class, // which I assume hides the math scope class // error: incomplete type--I assume this means the above // doesn't declare math::Minus, but math::<anon>::Minus //constexpr int twominus5= Minus<2>::value(); } // end anonymous namespace } // end math namespace //template <int I, int J> //class math::Minus; // error: invalid use of math::Minus w/o argument list template <int I, int J> int math::Minus<I,J>::value(){return I - J;} int main() { std::cout << math::Minus<5,1>::value() << std::endl << math::Minus<0>::value() << std::endl; } 

Output:

 4 -5 

... and the declaration rules seemed to match what I expected before reading this little snippet of the standard. Clearly, my understanding is somewhere wrong. Is this with my initial reading of the standard argument argument declaration default argument for the standard C ++ template, as I suspect, or did I skip some method of declaring the templates outside their own scope?

Naturally, an odd corner of the language like this (if it really exists) should be used with restraint and with great care, especially since it will change the behavior of partially defined patterns in other places depending on the most applicable area (can this cause would the full-resolved name otherwise be resolved in the area without default declarations if there were default parameters in the template definition area?), but it aroused my curiosity.

I will use pseudonyms regardless of the fact that this is less ambiguous for everyone involved, but as I said above: I am now wondering if this is a feature of an odd language that I could not fully use for intentional use, or a non-function I just imagined.

+6
source share
2 answers

I'm not sure I can fully follow your thoughts, but I believe that the standard simply uses too clear a wording. This probably means that the "same" template in another area may have different default settings. Example:

 namespace A { template< int = 42 > struct X; } namespace B { template< int = 123 > struct X; namespace C { template< int = 0 > struct X; } } 

Of course, these templates are not the same template (even if you think that a beginner might think so at first glance), but they are different templates. The standard wording is most likely intended only to emphasize this.

As an example, where you may have different default values, a template alias with using :

 #include <iostream> #include <type_traits> namespace A { template< int I = 42 > struct X { static void f() { std::cout << I << std::endl; } }; } namespace B { template< int I = 0 > using X = A::X< I >; } int main() { A::X<>::f(); B::X<>::f(); static_assert( std::is_same< B::X<>, A::X<0> >::value, "Oops" ); } 

Live example

The problem is that it looks like it matches your description first, but interesting, while B::X<> and A::X<0> are the same type , B::X is currently not the same pattern as A::X See more details.

There is a DR ( CWG issue 1286 ) to fix this. Different default OTOH parameters are a problem mentioned in DR, so even if DR is resolved, it may not allow different default values.

+2
source

One example of different default arguments in different areas for the same template is when they are declared in different translation units:

 // x.hpp template <class T> class X; // a.cpp #include "x.hpp" template <class T = int> class X; // b.cpp #include "x.hpp" template <class T = float> class X; 

Here we have two translation units that create two areas that declare the same entity (class X template). And in every global area, we have a new X declaration with different default template options. This is normal because they are located in different areas.

0
source

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


All Articles