"Backporting" nullptr to C++-pre-C++0x programs
Asked Answered
M

2

10

More or less what the title suggests. While I'm not yet using C++0x I'd like to be prepared for when it happens, and I'd also like to reduce the amount of code I have to rewrite to use some of its facilities. That way I can get backwards and forwards compatibility in one go.

One of the most interesting ones I have found is nullptr, which I've been using more often recently.

After checking the "Official workaround" and Meyer's suggestion, I decided that I'd like to use this in both my C++ and future C++0x programs. The second part is simple -- being a keyword, nullptr will simply be supported. But the first part is causing me some discomfort.

The Meyers proposal functions like this:

class nullptr_t { // ← this is my issue
    // definition of nullptr_t
} nullptr = { };

The problem with that proposal is that it declares the type to be declared as std::nullptr_t as required by C++0x. Which means for the workaround to "feel native" it has to be done by reopening the std:: namespace to add a type. I have the understanding that is illegal to do in a C++ program (unlike adding specializations which is apparently frown-and-let-go-with-a-warning).

I want to use nullptr in a comfortable AND legal way in a C++ program. One option I had thought of was declaring the type in another namespace and then bring it in using using:

namespace mylibrary {
class nullptr_t {
    ....
} nullptr = { };
// end namespace
}

// These would have to go in the header file.
using mylibrary::nullptr;
using mylibrary::nullptr_t; // apparently this is necessary as well?

Would this be the correct way to make it work? It would force usingdirectives, which also forces a specific order of #include directives as well. Would I be right to expect that no pre-C++0x code would request the type nullptr_t with namespace (as a function argument type, for example)? Would it actually work "feeling native" if it is done this way?


As an addendum, is it a welcomed or frowned upon thing to try and backport some nifty C++0x things to C++ for better compatibility and coding? In the meantime I have integrated this solution and other ones I'm working on in a piece of software to be released.

Melar answered 5/1, 2012 at 17:37 Comment(0)
S
1

The main reason you are not allowed to add things to namespace std is so you don't mess things up which are already there. This could otherwise easily be done. Something I found in the past were multiple definitions for output operators for containers instantiated with a built-in type like std::vector<int>. However, nullptr_t isn't defined in this namespace and adding a typedef should be pretty harmless. That is, I would define nullptr_t in a different namespace than namespace std and then add a typedef for nullptr_t to namespace std. The variable nullptr needs to be declared at global scope anyway as it is used unqualified in C++2011.

Whether the type std::nullptr_t is needed depends on whether you are interest in adding the signatures using this type. For example, std::shared_ptr<T> can be compared against nullptr. For this, it is necessary to add suitable overloads which mention the type std::nullptr_t (or use some other name for this type).

Strongbox answered 5/1, 2012 at 20:1 Comment(7)
I always thought the reason you can't add things to namespace std isn't so you don't mess up things that are there, but so that your code will compile on many compilers (who might be using the name you chose), and so it continues to compile when the standard adds stuff to the std namespace.Forgetful
(1) I believe the namespace is reserved for the implementation so, <iostream> is allowed to do whatever it wants in std. (2) This is so that if my code only compiles with one standard library, it can compile with all other conforming implementations of the standard library. (3) All names ending in _t are reserved for the implementation in C++03.Forgetful
From what I understand, the "underscore" rule exists precisely so that "implementations" use those names freely (just examine any copy of <algorithm>, so beyond those names provided by the Standard (such as cin, cout) no other name is reserved even in namespace std; at that point, a typedef can't harm forward compatibility than an actual declaration can, except it prevents us from adding too much stuff (like "detail namespaces").Melar
Ah, I was slightly off. From stackoverflow.com/questions/228783 "Some additional classes of identifier names are reserved for future extensions to the C language or the POSIX.1 environment. While using these names for your own purposes right now might not cause a problem, they do raise the possibility of conflict with future versions of the C or POSIX standards, so you should avoid these names. ... Names that end with '_t' are reserved"Forgetful
@DietmarKühl: I checked, C++11 doesn't reserve them, just POSIX.Forgetful
I can live with POSIX. I'd guess they'd be smart enough to not try to define a nullptr_t type. So, if I'm understanding it the right way, the way to "do it" would be to define a nullptr_t macro, or a typedef?Melar
Oh thanks! That seems to be what I need to implements this definitively then.Melar
T
3

Unless you can think of a reason you need to declare another object of type nullptr_t (I can't), I would hide the type away and put nullptr in the global namespace to mimic a keyword as closely as possible:

namespace mylibrary
{
    class nullptr_t
    {
        ....
    };
}

mylibrary::nullptr_t nullptr = { };
Titbit answered 5/1, 2012 at 18:21 Comment(1)
Yes, hiding the type away is what I want to do. I'm more or less concerned if there is any reason why someone would need to use the type nullptr_t for anything that can not be done by simply passing nullptr or a pointer type.Melar
S
1

The main reason you are not allowed to add things to namespace std is so you don't mess things up which are already there. This could otherwise easily be done. Something I found in the past were multiple definitions for output operators for containers instantiated with a built-in type like std::vector<int>. However, nullptr_t isn't defined in this namespace and adding a typedef should be pretty harmless. That is, I would define nullptr_t in a different namespace than namespace std and then add a typedef for nullptr_t to namespace std. The variable nullptr needs to be declared at global scope anyway as it is used unqualified in C++2011.

Whether the type std::nullptr_t is needed depends on whether you are interest in adding the signatures using this type. For example, std::shared_ptr<T> can be compared against nullptr. For this, it is necessary to add suitable overloads which mention the type std::nullptr_t (or use some other name for this type).

Strongbox answered 5/1, 2012 at 20:1 Comment(7)
I always thought the reason you can't add things to namespace std isn't so you don't mess up things that are there, but so that your code will compile on many compilers (who might be using the name you chose), and so it continues to compile when the standard adds stuff to the std namespace.Forgetful
(1) I believe the namespace is reserved for the implementation so, <iostream> is allowed to do whatever it wants in std. (2) This is so that if my code only compiles with one standard library, it can compile with all other conforming implementations of the standard library. (3) All names ending in _t are reserved for the implementation in C++03.Forgetful
From what I understand, the "underscore" rule exists precisely so that "implementations" use those names freely (just examine any copy of <algorithm>, so beyond those names provided by the Standard (such as cin, cout) no other name is reserved even in namespace std; at that point, a typedef can't harm forward compatibility than an actual declaration can, except it prevents us from adding too much stuff (like "detail namespaces").Melar
Ah, I was slightly off. From stackoverflow.com/questions/228783 "Some additional classes of identifier names are reserved for future extensions to the C language or the POSIX.1 environment. While using these names for your own purposes right now might not cause a problem, they do raise the possibility of conflict with future versions of the C or POSIX standards, so you should avoid these names. ... Names that end with '_t' are reserved"Forgetful
@DietmarKühl: I checked, C++11 doesn't reserve them, just POSIX.Forgetful
I can live with POSIX. I'd guess they'd be smart enough to not try to define a nullptr_t type. So, if I'm understanding it the right way, the way to "do it" would be to define a nullptr_t macro, or a typedef?Melar
Oh thanks! That seems to be what I need to implements this definitively then.Melar

© 2022 - 2024 — McMap. All rights reserved.