3

Which of them is the correct one and why?

I think it's the first one because Ts already have the && or const & associated with the type but I like to be sure and there really isn't that many examples of this particular case of noexcept.

template <typename T>
struct Test {
    T test;

    // A
    template <typename... Ts>
    Test(Ts &&... ts) noexcept(std::is_nothrow_constructible_v<T, Ts...>)
        : test(std::forward<Ts>(ts)...) {
    }

    // B
    template <typename... Ts>
    Test(Ts &&... ts) noexcept(std::is_nothrow_constructible_v<T, Ts &&...>)
        : test(std::forward<Ts>(ts)...) {
    }
};
João Pires
  • 927
  • 1
  • 5
  • 16

1 Answers1

4

I prefer option C. You can use the noexcept operator in the noexcept specifier and have it evaluate the call for you. I find this a lot easier to read an understand since you use the expression you want to do for exception specifier. In you case that would look like

template <typename T>
struct Test {
    T test;

    // C
    template <typename... Ts>
    Test(Ts &&... ts) noexcept(noexcept(T(std::forward<Ts>(ts)...)))
        : test(std::forward<Ts>(ts)...) {
    }
};

And now the code says Test(Ts &&... ts) is noexcept if T(std::forward<Ts>(ts)...)is noexcept.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Ah, but then you must've forgot, you can also use the `noexcept` identifier inside the `noexcept` operator inside the `noexcept` specifier, like so : `noexcept(noexcept(noexcept(yesexcept)));`. – Hatted Rooster Apr 10 '19 at 17:23
  • 2
    @SombreroChicken *noexception*? – NathanOliver Apr 10 '19 at 17:24
  • @NathanOliver, but from the options I gave, which of them would give me the same result? – João Pires Apr 10 '19 at 17:43
  • @JoãoPires I believe A is the correct way to do it since it will either be a `T` or `T&`. Can't sayy 100% though so I didn't want to make it part of the answer. That is why I like C best. I don't have to think about what `Ts` is and how it will play with `decval`. – NathanOliver Apr 10 '19 at 17:59