15

According to What are the reasons that extending the std namespace is considered undefined behavior?, adding anything to namespace std is Undefined Behavior, with some exceptions carved out, such as specializing std::hash. The primary reason for this is to allow the namespace to grow in future standards without risking collisions with user-written code.

Therefore, is it allowed to backport a specific feature to older standards, provided there are no language restrictions forbidding it? It clearly cannot collide with something that would be defined later.

As a concrete example, let's take:

namespace backport {
#if __cplusplus < 202002L
    template <typename T>
    struct type_identity { using type = T; };
#else
    using ::std::type_identity<T>;
#endif
}

static_assert(std::is_same<backport::type_identity<int>::type, int>::value, "");

This safely works in all of C++11 up to C++26 (C++03 does not have <type_traits>). Could I also safely write it without the extra namespace?

#if __cplusplus < 202002L
namespace std {
    template <typename T>
    struct type_identity { using type = T; };
}
#endif

static_assert(std::is_same<std::type_identity<int>::type, int>::value, "");
7
  • 8
    The rationale exlains why a certain rules was put in place, but once it's there it applies as specified (and not necessarily according to the original rationale). Since the rule is that adding to std (unless explicitly allowed) is UB - you have UB here. Commented Sep 24 at 11:26
  • Side note : I wouldn't even do the using statement in the first solution. Just use backport namespace everywhere until you can explicitly change to std::version later (find/replace). You will never be sure if your own version will be exactly the same (semantically too) to the version you are going to get. At least sometimes I backport simplified versions of future standards which are good enough for now, and then I want to do a line by line update I can test (instead of the big bang using you are doing) Commented Sep 24 at 11:42
  • 1
    @wohlstad Could you please explain the reason for editing the question? It looks to me like both versions are rendered exactly the same. Commented Sep 24 at 11:42
  • 1
    @DominikKaszewski I just thought it is nicer to have the links organized at the bottom and I wasn't sure if you are ware of this option. Feel free to rollback if you disagree. Commented Sep 24 at 11:44
  • 2
    @DominikKaszewski Well the issues it that the interaction with for example algorithms can be very subtle and unexpected. So yes I think the standard committee errs on the side of caution here by declaring almost everything added to std namespace as UB. So I too rather type something explicit now that I can explictly replace later (step by step) and test again. And as far as Hyrum goes... he's always looking or acting through colleagues who are not as careful as you (or just by using your code in unexpected ways ;) ) Commented Sep 24 at 12:36

1 Answer 1

22

TL;DR:

Your additions to namespace std invoke undefined behavior.

More info:

You might be right about the original rationale for introducing the rule disallowing to add stuff to namespace std.
It's nice to know the background for it, and it can be important when a change of the rule is discussed in the C++ committee.

But practically it does not really matter for determining whether you have UB or not in a given case:
Once a rule is added to the standard it is valid regardless of whether a specific case it applies to fits the original rationale.

Here the rule is very simple:

From [namespace.std]:

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified ...

Your case does not fall within the specified exceptions and therefore the behavior is undefined.

Sign up to request clarification or add additional context in comments.

2 Comments

Practically it very much does matter. If anything, it's only theoretically irrelevant. E.g., there have been cases where UB was unintentional and a subsequent defect report was filed to fix the issue in previous standards - and understanding the intent helps with understanding when that's the case, and thus with understanding when you can rely on the implementation's behavior before the DR is issued. Furthermore, understanding when UB is useless to the compiler gives you an idea of when you can practically rely on it with your particular compiler, despite the theoretical issues.
I agree in general. I meant that in order to answer to question whether this is UB (which is what the OP asked), the background is interesting but what matters is that the rule is in place. Of course the rule might change, and the committee can definatly consider the rationale when discussing the change. Anyway - editted to clarify.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.