Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why aren't template specializations allowed to be in different namespaces?

Please, see what I am trying to do:

#include <iostream>
namespace first
{
 template <class T>
 class myclass
 { 
  T t;
 public:
  void who_are_you() const
  { std::cout << "first::myclass"; }
 };
}
namespace second
{
 using first::myclass;
 template <>
 class myclass <int>
 {
  int i, j;
 public:
  void who_are_you() const
  { std::cout << "second::myclass"; }
 };
}

This isn't allowed. Could you please, clarify why can't specializations be in different namespaces, and what are the available solutions? Also, is it something fixed in C++0x?

This would allow me for example, to specialize std::max, std::swap, std::numeric_limits, etc.. without resorting to undefined behavior by adding something to ::std::?


@AndreyT Here is how I though I would use it:

// my_integer is a class
std::numeric_limits<my_integer>::max(); // specialized std::numeric_limits for my_integer

Can this be done?

like image 895
Khaled Alshaya Avatar asked Jun 18 '10 18:06

Khaled Alshaya


People also ask

Can you template a namespace?

It is a template used to create one or more namespaces. A usage of that template would be a “namespace template instantiation” which is a namespace. This is like how a usage of a “class template” is a “class template instantiation” which is a class type.

Why does the need of template specialization arise?

It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.

What is meant by template specialization in C++?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.


1 Answers

C++ 2003, §17.4.3.1/1: "A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library template results in undefined behavior unless the declaration depends on a user-defined name of external linkage and unless the specialization meets the standard library requirements for the original template."

As such, you're allowed to specialize a library template, and put your specialization in namespace std, as long as it depends on a user defined type and meets the requirements of the original template.

The code you have in your edited question seems to be a specialization for a user-defined name that (presumably) has external linkage, so you shouldn't have any problem with that part of things.

That leaves only the requirement that your specialization meet the requirements of the original template. For your type, most of this will probably border on trivial. The only part I can see that might not be obvious is that you do seem to have to provide a specialization for the whole template, not just numeric_limits::max(). I.e., you'll have to do something like (example should be in the ballpark for a 128-bit unsigned integer type):

namespace std { 
template <>
class numeric_limits<my_integer> {
public:

    static const bool is_specialized = true;
    static T min() throw() { return 0;
    static T max() throw() { return /* 2^128-1 */; } // ***
    static const int digits = 128;
    static const int digits10 = 38;
    static const bool is_signed = false;
    static const bool is_integer = true;
    static const bool is_exact = true;
    static const int radix = 2;
    static T epsilon() throw() { return 0; }
    static T round_error() throw() { return 0; }
    static const int min_exponent = 0;
    static const int min_exponent10 = 0;
    static const int max_exponent = 0;
    static const int max_exponent10 = 0;
    static const bool has_infinity = false;
    static const bool has_quiet_NaN = false;
    static const bool has_signaling_NaN = false;
    static const float_denorm_style has_denorm = denorm_absent;
    static const bool has_denorm_loss = false;
    static T infinity() throw() { return 0; }
    static T quiet_NaN() throw() { return 0; }
    static T signaling_NaN() throw() { return 0; }
    static T denorm_min() throw() { return 0; }
    static const bool is_iec559 = false;
    static const bool is_bounded = true;
    static const bool is_modulo = true;
    static const bool traps = false;
    static const bool tinyness_before = false;
    static const float_round_style round_style = round_toward_zero;
};
}

Quite a few of those are really for FP types, and aren't required to be meaningful for an integer type; I believe they still need to be implemented.

like image 140
Jerry Coffin Avatar answered Sep 29 '22 13:09

Jerry Coffin