0

As far as I can tell the code below asserts the same thing in two different ways:

#include <type_traits>

template<class T, class U>
concept nothrow_assignable = requires(T a, U b)
{
    requires noexcept(a = b);
};

template<class T>
void foo()
{
    // new way
    static_assert(nothrow_assignable<T, T>);

    // old way
    static_assert(std::is_nothrow_assignable_v<T, T>);
}

is there any reason to ever prefer the old way? The type_traits header is 3K+ lines of code, and this particular trait ends up invoking a deep tree of other traits (the definition is surprisingly involved, checking if the type is an array, a reference, a pointer, a function, etc and each of those checks instantiates a few more) so I would prefer not to use it for the sake of keeping my compile times low. But at the same time I've noticed that there don't appear to be standard concepts for every standard type trait (I have to implement nothrow_assignable myself) which seems like an obvious oversight so I'm wondering if there is a trade off here.

Joseph Garvin
  • 20,727
  • 18
  • 94
  • 165
  • It would be more obvious what is going on if you use the concept as a requirement rather than asserting on it. – Nicol Bolas Jul 18 '23 at 17:29
  • 2
    Your trait is broken, and reports `true` for `int[42], int[42]`. One reason to use the standard traits is that they're known to work. :P Rather than writing custom traits to improve your own build times, perhaps file a patch against your favorite C++ standard library, if profiling indeed indicates that it's a good change, to improve everyone's buidl times. – HolyBlackCat Jul 18 '23 at 17:31
  • @HolyBlackCat for my purposes returning true in that case is fine, I'm just trying to assert that a generated function with a noexcept specifier won't in fact throw an exception. Also I can't just file a patch for this -- whether the concept is standard is controlled by the standard committee. In general, nobody really believes pulling in a 3K line header has no impact on compile time, cmon, the expected value of that experiment is obviously negative. Big headers are a known bottleneck. – Joseph Garvin Jul 18 '23 at 18:54
  • Sorry, I meant not the concept, but rather keeping `constexpr bool` but replacing the initializer with this. *"In general, nobody really believes pulling in a 3K line header has no impact on compile time, cmon, the expected value of that experiment is obviously negative. Big headers are a known bottleneck."* My brain can't parse this sentence. Do you consider a 3K line header "big"? *"expected value of that experiment is obviously negative"* What does this mean? – HolyBlackCat Jul 18 '23 at 19:05
  • I don't know what "nobody believes", but without actually measuring, any discussion is based on FUD and so just pointless. With measurements, it's possible to invest time into something constructive, like determining if a standard header should be part of the precompiled header set, or not. – StoryTeller - Unslander Monica Jul 18 '23 at 19:07
  • I thought all major compilers used an `__is_nothrow_assignable` buildin, like in [libc++](https://github.com/llvm/llvm-project/blob/main/libcxx/include/__type_traits/is_nothrow_assignable.h). Are there others that do not, but still support concepts? – BoP Jul 18 '23 at 19:47
  • @HolyBlackCat I mean that the payoff of running the experiment isn't worth the time you would put into it; it's a bad use of time. We already know the more code you throw into the compiler the longer it will take, and practicing good header hygiene to keep build times low has been recommended best practice for ages. We're not going to find out including the type traits header is faster than not including it, we know no work takes less time than some work. – Joseph Garvin Jul 19 '23 at 16:18
  • @BoP GCC 10 supports concepts and doesn't appear to use a built-in. Might be different in newer versions. Even so, you would still need to include the type_traits header to invoke it. – Joseph Garvin Jul 19 '23 at 16:19
  • @StoryTeller-UnslanderMonica Even with a pre-compiled header you're only reducing the time to parse the code and build the compiler data structures -- they're still bigger and take more time to probe. Doing less work still beats doing more work. – Joseph Garvin Jul 19 '23 at 16:24
  • *"a bad use of time"* IMO, the bad use of time is reimplementing the traits by hand. This looks like a huge premature optimization, not "header hygiene". (I mean, if you're writing a library that's supposed to have minimal footprint, and only need a few of those checks, sure, write a custom concept. But if you're going to reimplement dozens of traits by hand, without profiling, this starts to look unreasonable.) *"3K line header"* The friggin `` alone is 22752 lines those days in my GCC, and it includes `` too. – HolyBlackCat Jul 19 '23 at 17:21
  • @HolyBlackCat There is some time cost implementing things yourself but I've spent 10x more time discussing it with you than writing the ~5 lines that work for my purposes. I've worked on many large C++ code bases and header hygiene has become an issue on every single one. And yes, I'm not using `` either, at least not in any of the code that I expect to be iterating on and recompiling a lot. Reducing comp time by 1 second for 40 devs that do 50 compiles a day is 140 hours saved per year. Many code bases get up to 10+ second incremental compiles b/c of lack of vigilance about this. – Joseph Garvin Jul 19 '23 at 17:54

0 Answers0